/*
===========================================================================
Copyright (C) 1999-2005 Id Software, Inc.
Copyright (C) 2000-2006 Tim Angus

This file is part of Tremulous.

Tremulous is free software; you can redistribute it
and/or modify it under the terms of the GNU General Public License as
published by the Free Software Foundation; either version 2 of the License,
or (at your option) any later version.

Tremulous is distributed in the hope that it will be
useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with Tremulous; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
===========================================================================
*/

// cg_draw.c -- draw all of the graphical elements during
// active (after loading) gameplay


#include "cg_local.h"
#include "../ui/ui_shared.h"

// used for scoreboard
extern    displayContextDef_t cgDC;
menuDef_t *menuScoreboard = NULL;

int drawTeamOverlayModificationCount = -1;

int   sortedTeamPlayers[ TEAM_MAXOVERLAY ];
int   numSortedTeamPlayers;

//TA UI
int CG_Text_Width( const char *text, float scale, int limit )
{
  int         count,len;
  float       out;
  glyphInfo_t *glyph;
  float       useScale;
// FIXME: see ui_main.c, same problem
//  const unsigned char *s = text;
  const char  *s = text;
  fontInfo_t  *font = &cgDC.Assets.textFont;

  if( scale <= cg_smallFont.value )
    font = &cgDC.Assets.smallFont;
  else if( scale > cg_bigFont.value )
    font = &cgDC.Assets.bigFont;

  useScale = scale * font->glyphScale;
  out = 0;

  if( text )
  {
    len = strlen( text );
    if( limit > 0 && len > limit )
      len = limit;

    count = 0;
    while( s && *s && count < len )
    {
      if( Q_IsColorString( s ) )
      {
        s += 2;
        continue;
      }
      else
      {
        glyph = &font->glyphs[ (int)*s ];
        //TTimo: FIXME: getting nasty warnings without the cast,
        //hopefully this doesn't break the VM build
        out += glyph->xSkip;
        s++;
        count++;
      }
    }
  }

  return out * useScale;
}

int CG_Text_Height( const char *text, float scale, int limit )
{
  int         len, count;
  float       max;
  glyphInfo_t *glyph;
  float       useScale;
// TTimo: FIXME
//  const unsigned char *s = text;
  const char  *s = text;
  fontInfo_t  *font = &cgDC.Assets.textFont;

  if( scale <= cg_smallFont.value )
    font = &cgDC.Assets.smallFont;
  else if( scale > cg_bigFont.value )
    font = &cgDC.Assets.bigFont;

  useScale = scale * font->glyphScale;
  max = 0;

  if( text )
  {
    len = strlen( text );
    if( limit > 0 && len > limit )
      len = limit;

    count = 0;
    while( s && *s && count < len )
    {
      if( Q_IsColorString( s ) )
      {
        s += 2;
        continue;
      }
      else
      {
        glyph = &font->glyphs[ (int)*s ];
        //TTimo: FIXME: getting nasty warnings without the cast,
        //hopefully this doesn't break the VM build
        if( max < glyph->height )
          max = glyph->height;

        s++;
        count++;
      }
    }
  }

  return max * useScale;
}

void CG_Text_PaintChar( float x, float y, float width, float height, float scale,
                        float s, float t, float s2, float t2, qhandle_t hShader )
{
  float w, h;
  w = width * scale;
  h = height * scale;
  CG_AdjustFrom640( &x, &y, &w, &h );
  trap_R_DrawStretchPic( x, y, w, h, s, t, s2, t2, hShader );
}

void CG_Text_Paint( float x, float y, float scale, vec4_t color, const char *text,
                    float adjust, int limit, int style )
{
  int         len, count;
  vec4_t      newColor;
  glyphInfo_t *glyph;
  float       useScale;
  fontInfo_t  *font = &cgDC.Assets.textFont;

  if( scale <= cg_smallFont.value )
    font = &cgDC.Assets.smallFont;
  else if( scale > cg_bigFont.value )
    font = &cgDC.Assets.bigFont;

  useScale = scale * font->glyphScale;
  if( text )
  {
// TTimo: FIXME
//    const unsigned char *s = text;
    const char *s = text;

    trap_R_SetColor( color );
    memcpy( &newColor[ 0 ], &color[ 0 ], sizeof( vec4_t ) );
    len = strlen( text );

    if( limit > 0 && len > limit )
      len = limit;

    count = 0;
    while( s && *s && count < len )
    {
      glyph = &font->glyphs[ (int)*s ];
      //TTimo: FIXME: getting nasty warnings without the cast,
      //hopefully this doesn't break the VM build

      if( Q_IsColorString( s ) )
      {
        memcpy( newColor, g_color_table[ ColorIndex( *( s + 1 ) ) ], sizeof( newColor ) );
        newColor[ 3 ] = color[ 3 ];
        trap_R_SetColor( newColor );
        s += 2;
        continue;
      }
      else
      {
        float yadj = useScale * glyph->top;
        if( style == ITEM_TEXTSTYLE_SHADOWED ||
            style == ITEM_TEXTSTYLE_SHADOWEDMORE )
        {
          int ofs = style == ITEM_TEXTSTYLE_SHADOWED ? 1 : 2;
          colorBlack[ 3 ] = newColor[ 3 ];
          trap_R_SetColor( colorBlack );
          CG_Text_PaintChar( x + ofs, y - yadj + ofs,
                             glyph->imageWidth,
                             glyph->imageHeight,
                             useScale,
                             glyph->s,
                             glyph->t,
                             glyph->s2,
                             glyph->t2,
                             glyph->glyph );

          colorBlack[ 3 ] = 1.0;
          trap_R_SetColor( newColor );
        }
        else if( style == ITEM_TEXTSTYLE_NEON )
        {
          vec4_t glow, outer, inner, white;

          glow[ 0 ] = newColor[ 0 ] * 0.5;
          glow[ 1 ] = newColor[ 1 ] * 0.5;
          glow[ 2 ] = newColor[ 2 ] * 0.5;
          glow[ 3 ] = newColor[ 3 ] * 0.2;

          outer[ 0 ] = newColor[ 0 ];
          outer[ 1 ] = newColor[ 1 ];
          outer[ 2 ] = newColor[ 2 ];
          outer[ 3 ] = newColor[ 3 ];

          inner[ 0 ] = newColor[ 0 ] * 1.5 > 1.0f ? 1.0f : newColor[ 0 ] * 1.5;
          inner[ 1 ] = newColor[ 1 ] * 1.5 > 1.0f ? 1.0f : newColor[ 1 ] * 1.5;
          inner[ 2 ] = newColor[ 2 ] * 1.5 > 1.0f ? 1.0f : newColor[ 2 ] * 1.5;
          inner[ 3 ] = newColor[ 3 ];

          white[ 0 ] = white[ 1 ] = white[ 2 ] = white[ 3 ] = 1.0f;

          trap_R_SetColor( glow );
          CG_Text_PaintChar(  x - 3, y - yadj - 3,
                              glyph->imageWidth + 6,
                              glyph->imageHeight + 6,
                              useScale,
                              glyph->s,
                              glyph->t,
                              glyph->s2,
                              glyph->t2,
                              glyph->glyph );

          trap_R_SetColor( outer );
          CG_Text_PaintChar(  x - 1, y - yadj - 1,
                              glyph->imageWidth + 2,
                              glyph->imageHeight + 2,
                              useScale,
                              glyph->s,
                              glyph->t,
                              glyph->s2,
                              glyph->t2,
                              glyph->glyph );

          trap_R_SetColor( inner );
          CG_Text_PaintChar(  x - 0.5, y - yadj - 0.5,
                              glyph->imageWidth + 1,
                              glyph->imageHeight + 1,
                              useScale,
                              glyph->s,
                              glyph->t,
                              glyph->s2,
                              glyph->t2,
                              glyph->glyph );

          trap_R_SetColor( white );
        }


        CG_Text_PaintChar( x, y - yadj,
                           glyph->imageWidth,
                           glyph->imageHeight,
                           useScale,
                           glyph->s,
                           glyph->t,
                           glyph->s2,
                           glyph->t2,
                           glyph->glyph );

        x += ( glyph->xSkip * useScale ) + adjust;
        s++;
        count++;
      }
    }

    trap_R_SetColor( NULL );
  }
}

/*
==============
CG_DrawFieldPadded

Draws large numbers for status bar and powerups
==============
*/
static void CG_DrawFieldPadded( int x, int y, int width, int cw, int ch, int value )
{
  char  num[ 16 ], *ptr;
  int   l, orgL;
  int   frame;
  int   charWidth, charHeight;

  if( !( charWidth = cw ) )
    charWidth = CHAR_WIDTH;

  if( !( charHeight = ch ) )
    charWidth = CHAR_HEIGHT;

  if( width < 1 )
    return;

  // draw number string
  if( width > 4 )
    width = 4;

  switch( width )
  {
    case 1:
      value = value > 9 ? 9 : value;
      value = value < 0 ? 0 : value;
      break;
    case 2:
      value = value > 99 ? 99 : value;
      value = value < -9 ? -9 : value;
      break;
    case 3:
      value = value > 999 ? 999 : value;
      value = value < -99 ? -99 : value;
      break;
    case 4:
      value = value > 9999 ? 9999 : value;
      value = value < -999 ? -999 : value;
      break;
  }

  Com_sprintf( num, sizeof( num ), "%d", value );
  l = strlen( num );

  if( l > width )
    l = width;

  orgL = l;

  x += 2;

  ptr = num;
  while( *ptr && l )
  {
    if( width > orgL )
    {
      CG_DrawPic( x,y, charWidth, charHeight, cgs.media.numberShaders[ 0 ] );
      width--;
      x += charWidth;
      continue;
    }

    if( *ptr == '-' )
      frame = STAT_MINUS;
    else
      frame = *ptr - '0';

    CG_DrawPic( x,y, charWidth, charHeight, cgs.media.numberShaders[ frame ] );
    x += charWidth;
    ptr++;
    l--;
  }
}

/*
==============
CG_DrawField

Draws large numbers for status bar and powerups
==============
*/
void CG_DrawField( float x, float y, int width, float cw, float ch, int value )
{
  char  num[ 16 ], *ptr;
  int   l;
  int   frame;
  float charWidth, charHeight;

  if( !( charWidth = cw ) )
    charWidth = CHAR_WIDTH;

  if( !( charHeight = ch ) )
    charWidth = CHAR_HEIGHT;

  if( width < 1 )
    return;

  // draw number string
  if( width > 4 )
    width = 4;

  switch( width )
  {
    case 1:
      value = value > 9 ? 9 : value;
      value = value < 0 ? 0 : value;
      break;
    case 2:
      value = value > 99 ? 99 : value;
      value = value < -9 ? -9 : value;
      break;
    case 3:
      value = value > 999 ? 999 : value;
      value = value < -99 ? -99 : value;
      break;
    case 4:
      value = value > 9999 ? 9999 : value;
      value = value < -999 ? -999 : value;
      break;
  }

  Com_sprintf( num, sizeof( num ), "%d", value );
  l = strlen( num );

  if( l > width )
    l = width;

  x += 2 + charWidth * ( width - l );

  ptr = num;
  while( *ptr && l )
  {
    if( *ptr == '-' )
      frame = STAT_MINUS;
    else
      frame = *ptr -'0';

    CG_DrawPic( x,y, charWidth, charHeight, cgs.media.numberShaders[ frame ] );
    x += charWidth;
    ptr++;
    l--;
  }
}

static void CG_DrawProgressBar( rectDef_t *rect, vec4_t color, float scale,
                                int align, int textStyle, int special, float progress )
{
  float   rimWidth = rect->h / 20.0f;
  float   doneWidth, leftWidth;
  float   tx, ty, tw, th;
  char    textBuffer[ 8 ];

  if( rimWidth < 0.6f )
    rimWidth = 0.6f;

  if( special >= 0.0f )
    rimWidth = special;

  if( progress < 0.0f )
    progress = 0.0f;
  else if( progress > 1.0f )
    progress = 1.0f;

  doneWidth = ( rect->w - 2 * rimWidth ) * progress;
  leftWidth = ( rect->w - 2 * rimWidth ) - doneWidth;

  trap_R_SetColor( color );

  //draw rim and bar
  if( align == ITEM_ALIGN_RIGHT )
  {
    CG_DrawPic( rect->x, rect->y, rimWidth, rect->h, cgs.media.whiteShader );
    CG_DrawPic( rect->x + rimWidth, rect->y,
      leftWidth, rimWidth, cgs.media.whiteShader );
    CG_DrawPic( rect->x + rimWidth, rect->y + rect->h - rimWidth,
      leftWidth, rimWidth, cgs.media.whiteShader );
    CG_DrawPic( rect->x + rimWidth + leftWidth, rect->y,
      rimWidth + doneWidth, rect->h, cgs.media.whiteShader );
  }
  else
  {
    CG_DrawPic( rect->x, rect->y, rimWidth + doneWidth, rect->h, cgs.media.whiteShader );
    CG_DrawPic( rimWidth + rect->x + doneWidth, rect->y,
      leftWidth, rimWidth, cgs.media.whiteShader );
    CG_DrawPic( rimWidth + rect->x + doneWidth, rect->y + rect->h - rimWidth,
      leftWidth, rimWidth, cgs.media.whiteShader );
    CG_DrawPic( rect->x + rect->w - rimWidth, rect->y, rimWidth, rect->h, cgs.media.whiteShader );
  }

  trap_R_SetColor( NULL );

  //draw text
  if( scale > 0.0 )
  {
    Com_sprintf( textBuffer, sizeof( textBuffer ), "%d%%", (int)( progress * 100 ) );
    tw = CG_Text_Width( textBuffer, scale, 0 );
    th = scale * 40.0f;

    switch( align )
    {
      case ITEM_ALIGN_LEFT:
        tx = rect->x + ( rect->w / 10.0f );
        ty = rect->y + ( rect->h / 2.0f ) + ( th / 2.0f );
        break;

      case ITEM_ALIGN_RIGHT:
        tx = rect->x + rect->w - ( rect->w / 10.0f ) - tw;
        ty = rect->y + ( rect->h / 2.0f ) + ( th / 2.0f );
        break;

      case ITEM_ALIGN_CENTER:
        tx = rect->x + ( rect->w / 2.0f ) - ( tw / 2.0f );
        ty = rect->y + ( rect->h / 2.0f ) + ( th / 2.0f );
        break;

      default:
        tx = ty = 0.0f;
    }

    CG_Text_Paint( tx, ty, scale, color, textBuffer, 0, 0, textStyle );
  }
}

//=============== TA: was cg_newdraw.c

#define NO_CREDITS_TIME 2000

static void CG_DrawPlayerCreditsValue( rectDef_t *rect, vec4_t color, qboolean padding )
{
  int           value;
  playerState_t *ps;
  centity_t     *cent;

  cent = &cg_entities[ cg.snap->ps.clientNum ];
  ps = &cg.snap->ps;

  //if the build timer pie is showing don't show this
  if( ( cent->currentState.weapon == WP_ABUILD ||
      cent->currentState.weapon == WP_ABUILD2 ) && ps->stats[ STAT_MISC ] )
    return;

  value = ps->persistant[ PERS_CREDIT ];
  if( value > -1 )
  {
    if( cg.predictedPlayerState.stats[ STAT_PTEAM ] == PTE_ALIENS &&
        !CG_AtHighestClass( ) )
    {
      if( cg.time - cg.lastEvolveAttempt <= NO_CREDITS_TIME )
      {
        if( ( ( cg.time - cg.lastEvolveAttempt ) / 300 ) % 2 )
          color[ 3 ] = 0.0f;
      }
    }

    trap_R_SetColor( color );

    if( padding )
      CG_DrawFieldPadded( rect->x, rect->y, 4, rect->w / 4, rect->h, value );
    else
      CG_DrawField( rect->x, rect->y, 1, rect->w, rect->h, value );

    trap_R_SetColor( NULL );
  }
}

static void CG_DrawPlayerBankValue( rectDef_t *rect, vec4_t color, qboolean padding )
{
  int           value;
  playerState_t *ps;

  ps = &cg.snap->ps;

  value = ps->persistant[ PERS_BANK ];
  if( value > -1 )
  {
    trap_R_SetColor( color );

    if( padding )
      CG_DrawFieldPadded( rect->x, rect->y, 4, rect->w / 4, rect->h, value );
    else
      CG_DrawField( rect->x, rect->y, 1, rect->w, rect->h, value );

    trap_R_SetColor( NULL );
  }
}

#define HH_MIN_ALPHA  0.2f
#define HH_MAX_ALPHA  0.8f
#define HH_ALPHA_DIFF (HH_MAX_ALPHA-HH_MIN_ALPHA)

#define AH_MIN_ALPHA  0.2f
#define AH_MAX_ALPHA  0.8f
#define AH_ALPHA_DIFF (AH_MAX_ALPHA-AH_MIN_ALPHA)

/*
==============
CG_DrawPlayerStamina1
==============
*/
static void CG_DrawPlayerStamina1( rectDef_t *rect, vec4_t color, qhandle_t shader )
{
  playerState_t *ps = &cg.snap->ps;
  float         stamina = ps->stats[ STAT_STAMINA ];
  float         maxStaminaBy3 = (float)MAX_STAMINA / 3.0f;
  float         progress;

  stamina -= ( 2 * (int)maxStaminaBy3 );
  progress = stamina / maxStaminaBy3;

  if( progress > 1.0f )
    progress  = 1.0f;
  else if( progress < 0.0f )
    progress = 0.0f;

  color[ 3 ] = HH_MIN_ALPHA + ( progress * HH_ALPHA_DIFF );

  trap_R_SetColor( color );
  CG_DrawPic( rect->x, rect->y, rect->w, rect->h, shader );
  trap_R_SetColor( NULL );
}

/*
==============
CG_DrawPlayerStamina2
==============
*/
static void CG_DrawPlayerStamina2( rectDef_t *rect, vec4_t color, qhandle_t shader )
{
  playerState_t *ps = &cg.snap->ps;
  float         stamina = ps->stats[ STAT_STAMINA ];
  float         maxStaminaBy3 = (float)MAX_STAMINA / 3.0f;
  float         progress;

  stamina -= (int)maxStaminaBy3;
  progress = stamina / maxStaminaBy3;

  if( progress > 1.0f )
    progress  = 1.0f;
  else if( progress < 0.0f )
    progress = 0.0f;

  color[ 3 ] = HH_MIN_ALPHA + ( progress * HH_ALPHA_DIFF );

  trap_R_SetColor( color );
  CG_DrawPic( rect->x, rect->y, rect->w, rect->h, shader );
  trap_R_SetColor( NULL );
}

/*
==============
CG_DrawPlayerStamina3
==============
*/
static void CG_DrawPlayerStamina3( rectDef_t *rect, vec4_t color, qhandle_t shader )
{
  playerState_t *ps = &cg.snap->ps;
  float         stamina = ps->stats[ STAT_STAMINA ];
  float         maxStaminaBy3 = (float)MAX_STAMINA / 3.0f;
  float         progress;

  progress = stamina / maxStaminaBy3;

  if( progress > 1.0f )
    progress  = 1.0f;
  else if( progress < 0.0f )
    progress = 0.0f;

  color[ 3 ] = HH_MIN_ALPHA + ( progress * HH_ALPHA_DIFF );

  trap_R_SetColor( color );
  CG_DrawPic( rect->x, rect->y, rect->w, rect->h, shader );
  trap_R_SetColor( NULL );
}

/*
==============
CG_DrawPlayerStamina4
==============
*/
static void CG_DrawPlayerStamina4( rectDef_t *rect, vec4_t color, qhandle_t shader )
{
  playerState_t *ps = &cg.snap->ps;
  float         stamina = ps->stats[ STAT_STAMINA ];
  float         progress;

  stamina += (float)MAX_STAMINA;
  progress = stamina / (float)MAX_STAMINA;

  if( progress > 1.0f )
    progress  = 1.0f;
  else if( progress < 0.0f )
    progress = 0.0f;

  color[ 3 ] = HH_MIN_ALPHA + ( progress * HH_ALPHA_DIFF );

  trap_R_SetColor( color );
  CG_DrawPic( rect->x, rect->y, rect->w, rect->h, shader );
  trap_R_SetColor( NULL );
}

/*
==============
CG_DrawPlayerStaminaBolt
==============
*/
static void CG_DrawPlayerStaminaBolt( rectDef_t *rect, vec4_t color, qhandle_t shader )
{
  playerState_t *ps = &cg.snap->ps;
  float         stamina = ps->stats[ STAT_STAMINA ];

  if( stamina < 0 )
    color[ 3 ] = HH_MIN_ALPHA;
  else
    color[ 3 ] = HH_MAX_ALPHA;

  trap_R_SetColor( color );
  CG_DrawPic( rect->x, rect->y, rect->w, rect->h, shader );
  trap_R_SetColor( NULL );
}

/*
==============
CG_DrawPlayerClipsRing
==============
*/
static void CG_DrawPlayerClipsRing( rectDef_t *rect, vec4_t color, qhandle_t shader )
{
  playerState_t *ps = &cg.snap->ps;
  centity_t     *cent;
  float         buildTime = ps->stats[ STAT_MISC ];
  float         progress;
  float         maxDelay;

  cent = &cg_entities[ cg.snap->ps.clientNum ];

  switch( cent->currentState.weapon )
  {
    case WP_ABUILD:
    case WP_ABUILD2:
    case WP_HBUILD:
    case WP_HBUILD2:
      maxDelay = (float)BG_FindBuildDelayForWeapon( cent->currentState.weapon );

      if( buildTime > maxDelay )
        buildTime = maxDelay;

      progress = ( maxDelay - buildTime ) / maxDelay;

      color[ 3 ] = HH_MIN_ALPHA + ( progress * HH_ALPHA_DIFF );
      break;

    default:
      if( ps->weaponstate == WEAPON_RELOADING )
      {
        maxDelay = (float)BG_FindReloadTimeForWeapon( cent->currentState.weapon );
        progress = ( maxDelay - (float)ps->weaponTime ) / maxDelay;

        color[ 3 ] = HH_MIN_ALPHA + ( progress * HH_ALPHA_DIFF );
      }
      break;
  }

  trap_R_SetColor( color );
  CG_DrawPic( rect->x, rect->y, rect->w, rect->h, shader );
  trap_R_SetColor( NULL );
}

/*
==============
CG_DrawPlayerBuildTimerRing
==============
*/
static void CG_DrawPlayerBuildTimerRing( rectDef_t *rect, vec4_t color, qhandle_t shader )
{
  playerState_t *ps = &cg.snap->ps;
  centity_t     *cent;
  float         buildTime = ps->stats[ STAT_MISC ];
  float         progress;
  float         maxDelay;

  cent = &cg_entities[ cg.snap->ps.clientNum ];

  maxDelay = (float)BG_FindBuildDelayForWeapon( cent->currentState.weapon );

  if( buildTime > maxDelay )
    buildTime = maxDelay;

  progress = ( maxDelay - buildTime ) / maxDelay;

  color[ 3 ] = AH_MIN_ALPHA + ( progress * AH_ALPHA_DIFF );

  trap_R_SetColor( color );
  CG_DrawPic( rect->x, rect->y, rect->w, rect->h, shader );
  trap_R_SetColor( NULL );
}

/*
==============
CG_DrawPlayerBoosted
==============
*/
static void CG_DrawPlayerBoosted( rectDef_t *rect, vec4_t color, qhandle_t shader )
{
  playerState_t *ps = &cg.snap->ps;
  qboolean      boosted = ps->stats[ STAT_STATE ] & SS_BOOSTED;

  if( boosted )
    color[ 3 ] = AH_MAX_ALPHA;
  else
    color[ 3 ] = AH_MIN_ALPHA;

  trap_R_SetColor( color );
  CG_DrawPic( rect->x, rect->y, rect->w, rect->h, shader );
  trap_R_SetColor( NULL );
}

/*
==============
CG_DrawPlayerBoosterBolt
==============
*/
static void CG_DrawPlayerBoosterBolt( rectDef_t *rect, vec4_t color, qhandle_t shader )
{
  playerState_t *ps = &cg.snap->ps;
  qboolean      boosted = ps->stats[ STAT_STATE ] & SS_BOOSTED;
  vec4_t        localColor;

  Vector4Copy( color, localColor );

  if( boosted )
  {
    if( ps->stats[ STAT_BOOSTTIME ] > BOOST_TIME - 3000 )
    {
      qboolean flash = ( ps->stats[ STAT_BOOSTTIME ] / 500 ) % 2;

      if( flash )
        localColor[ 3 ] = 1.0f;
    }
  }

  trap_R_SetColor( localColor );
  CG_DrawPic( rect->x, rect->y, rect->w, rect->h, shader );
  trap_R_SetColor( NULL );
}

/*
==============
CG_DrawPlayerPoisonBarbs
==============
*/
static void CG_DrawPlayerPoisonBarbs( rectDef_t *rect, vec4_t color, qhandle_t shader )
{
  playerState_t *ps = &cg.snap->ps;
  int           x = rect->x;
  int           y = rect->y;
  int           width = rect->w;
  int           height = rect->h;
  qboolean      vertical;
  int           iconsize, numBarbs, i;

  BG_UnpackAmmoArray( ps->weapon, ps->ammo, ps->powerups, &numBarbs, NULL );

  if( height > width )
  {
    vertical = qtrue;
    iconsize = width;
  }
  else if( height <= width )
  {
    vertical = qfalse;
    iconsize = height;
  }

  if( color[ 3 ] != 0.0 )
    trap_R_SetColor( color );

  for( i = 0; i < numBarbs; i ++ )
  {
    if( vertical )
      y += iconsize;
    else
      x += iconsize;

    CG_DrawPic( x, y, iconsize, iconsize, shader );
  }

  trap_R_SetColor( NULL );
}

/*
==============
CG_DrawPlayerWallclimbing
==============
*/
static void CG_DrawPlayerWallclimbing( rectDef_t *rect, vec4_t color, qhandle_t shader )
{
  playerState_t *ps = &cg.snap->ps;
  qboolean      ww = ps->stats[ STAT_STATE ] & SS_WALLCLIMBING;

  if( ww )
    color[ 3 ] = AH_MAX_ALPHA;
  else
    color[ 3 ] = AH_MIN_ALPHA;

  trap_R_SetColor( color );
  CG_DrawPic( rect->x, rect->y, rect->w, rect->h, shader );
  trap_R_SetColor( NULL );
}

static void CG_DrawPlayerStamina( rectDef_t *rect, vec4_t color, float scale,
                                  int align, int textStyle, int special )
{
  playerState_t *ps = &cg.snap->ps;
  int           stamina = ps->stats[ STAT_STAMINA ];
  float         progress = ( (float)stamina + (float)MAX_STAMINA ) / ( (float)MAX_STAMINA * 2.0f );

  CG_DrawProgressBar( rect, color, scale, align, textStyle, special, progress );
}

static void CG_DrawPlayerAmmoValue( rectDef_t *rect, vec4_t color )
{
  int           value;
  centity_t     *cent;
  playerState_t *ps;

  cent = &cg_entities[ cg.snap->ps.clientNum ];
  ps = &cg.snap->ps;

  if( cent->currentState.weapon )
  {
    switch( cent->currentState.weapon )
    {
      case WP_ABUILD:
      case WP_ABUILD2:
        //percentage of BP remaining
        value = cgs.alienBuildPoints;
        break;

      case WP_HBUILD:
      case WP_HBUILD2:
        //percentage of BP remaining
        value = cgs.humanBuildPoints;
        break;

      default:
        BG_UnpackAmmoArray( cent->currentState.weapon, ps->ammo, ps->powerups, &value, NULL );
        break;
    }

    if( value > 999 )
      value = 999;

    if( value > -1 )
    {
      trap_R_SetColor( color );
      CG_DrawField( rect->x, rect->y, 4, rect->w / 4, rect->h, value );
      trap_R_SetColor( NULL );
    }
  }
}


/*
==============
CG_DrawAlienSense
==============
*/
static void CG_DrawAlienSense( rectDef_t *rect )
{
  if( BG_ClassHasAbility( cg.snap->ps.stats[ STAT_PCLASS ], SCA_ALIENSENSE ) )
    CG_AlienSense( rect );
}


/*
==============
CG_DrawHumanScanner
==============
*/
static void CG_DrawHumanScanner( rectDef_t *rect, qhandle_t shader, vec4_t color )
{
  if( BG_InventoryContainsUpgrade( UP_HELMET, cg.snap->ps.stats ) )
    CG_Scanner( rect, shader, color );
}


/*
==============
CG_DrawUsableBuildable
==============
*/
static void CG_DrawUsableBuildable( rectDef_t *rect, qhandle_t shader, vec4_t color )
{
  vec3_t        view, point;
  trace_t       trace;
  entityState_t *es;

  AngleVectors( cg.refdefViewAngles, view, NULL, NULL );
  VectorMA( cg.refdef.vieworg, 64, view, point );
  CG_Trace( &trace, cg.refdef.vieworg, NULL, NULL,
            point, cg.predictedPlayerState.clientNum, MASK_SHOT );

  es = &cg_entities[ trace.entityNum ].currentState;

  if( es->eType == ET_BUILDABLE && BG_FindUsableForBuildable( es->modelindex ) &&
      cg.predictedPlayerState.stats[ STAT_PTEAM ] == BG_FindTeamForBuildable( es->modelindex ) )
  {
    //hack to prevent showing the usable buildable when you aren't carrying an energy weapon
    if( ( es->modelindex == BA_H_REACTOR || es->modelindex == BA_H_REPEATER ) &&
        ( !BG_FindUsesEnergyForWeapon( cg.snap->ps.weapon ) ||
          BG_FindInfinteAmmoForWeapon( cg.snap->ps.weapon ) ) )
      return;

    trap_R_SetColor( color );
    CG_DrawPic( rect->x, rect->y, rect->w, rect->h, shader );
    trap_R_SetColor( NULL );
  }
}


#define BUILD_DELAY_TIME  2000

static void CG_DrawPlayerBuildTimer( rectDef_t *rect, vec4_t color )
{
  float         progress;
  int           index;
  centity_t     *cent;
  playerState_t *ps;

  cent = &cg_entities[ cg.snap->ps.clientNum ];
  ps = &cg.snap->ps;

  if( cent->currentState.weapon )
  {
    switch( cent->currentState.weapon )
    {
      case WP_ABUILD:
        progress = (float)ps->stats[ STAT_MISC ] / (float)ABUILDER_BASE_DELAY;
        break;

      case WP_ABUILD2:
        progress = (float)ps->stats[ STAT_MISC ] / (float)ABUILDER_ADV_DELAY;
        break;

      case WP_HBUILD:
        progress = (float)ps->stats[ STAT_MISC ] / (float)HBUILD_DELAY;
        break;

      case WP_HBUILD2:
        progress = (float)ps->stats[ STAT_MISC ] / (float)HBUILD2_DELAY;
        break;

      default:
        return;
        break;
    }

    if( !ps->stats[ STAT_MISC ] )
      return;

    index = (int)( progress * 8.0f );

    if( index > 7 )
      index = 7;
    else if( index < 0 )
      index = 0;

    if( cg.time - cg.lastBuildAttempt <= BUILD_DELAY_TIME )
    {
      if( ( ( cg.time - cg.lastBuildAttempt ) / 300 ) % 2 )
      {
        color[ 0 ] = 1.0f;
        color[ 1 ] = color[ 2 ] = 0.0f;
        color[ 3 ] = 1.0f;
      }
    }

    trap_R_SetColor( color );
    CG_DrawPic( rect->x, rect->y, rect->w, rect->h,
      cgs.media.buildWeaponTimerPie[ index ] );
    trap_R_SetColor( NULL );
  }
}

static void CG_DrawPlayerClipsValue( rectDef_t *rect, vec4_t color )
{
  int           value;
  centity_t     *cent;
  playerState_t *ps;

  cent = &cg_entities[ cg.snap->ps.clientNum ];
  ps = &cg.snap->ps;

  if( cent->currentState.weapon )
  {
    switch( cent->currentState.weapon )
    {
      case WP_ABUILD:
      case WP_ABUILD2:
      case WP_HBUILD:
      case WP_HBUILD2:
        break;

      default:
        BG_UnpackAmmoArray( cent->currentState.weapon, ps->ammo, ps->powerups, NULL, &value );

        if( value > -1 )
        {
          trap_R_SetColor( color );
          CG_DrawField( rect->x, rect->y, 4, rect->w / 4, rect->h, value );
          trap_R_SetColor( NULL );
        }
        break;
    }
  }
}

static void CG_DrawPlayerHealthValue( rectDef_t *rect, vec4_t color )
{
  playerState_t *ps;
  int value;

  ps = &cg.snap->ps;

  value = ps->stats[ STAT_HEALTH ];

  trap_R_SetColor( color );
  CG_DrawField( rect->x, rect->y, 4, rect->w / 4, rect->h, value );
  trap_R_SetColor( NULL );
}

static void CG_DrawPlayerHealthBar( rectDef_t *rect, vec4_t color, float scale,
                                    int align, int textStyle, int special )
{
  playerState_t *ps;
  float total;

  ps = &cg.snap->ps;

  total = ( (float)ps->stats[ STAT_HEALTH ] / (float)ps->stats[ STAT_MAX_HEALTH ] );
  CG_DrawProgressBar( rect, color, scale, align, textStyle, special, total );
}

/*
==============
CG_DrawPlayerHealthCross
==============
*/
static void CG_DrawPlayerHealthCross( rectDef_t *rect, vec4_t color, qhandle_t shader )
{
  playerState_t *ps = &cg.snap->ps;
  int           health = ps->stats[ STAT_HEALTH ];

  if( health < 10 )
  {
    color[ 0 ] = 1.0f;
    color[ 1 ] = color[ 2 ] = 0.0f;
  }

  trap_R_SetColor( color );
  CG_DrawPic( rect->x, rect->y, rect->w, rect->h, shader );
  trap_R_SetColor( NULL );
}

static void CG_DrawProgressLabel( rectDef_t *rect, float text_x, float text_y, vec4_t color,
                                  float scale, int align, const char *s, float fraction )
{
  vec4_t white = { 1.0f, 1.0f, 1.0f, 1.0f };
  float tx, tw = CG_Text_Width( s, scale, 0 );

  switch( align )
  {
    case ITEM_ALIGN_LEFT:
      tx = 0.0f;
      break;

    case ITEM_ALIGN_RIGHT:
      tx = rect->w - tw;
      break;

    case ITEM_ALIGN_CENTER:
      tx = ( rect->w / 2.0f ) - ( tw / 2.0f );
      break;

    default:
      tx = 0.0f;
  }

  if( fraction < 1.0f )
    CG_Text_Paint( rect->x + text_x + tx, rect->y + text_y, scale, white,
      s, 0, 0, ITEM_TEXTSTYLE_NORMAL );
  else
    CG_Text_Paint( rect->x + text_x + tx, rect->y + text_y, scale, color,
      s, 0, 0, ITEM_TEXTSTYLE_NEON );
}

static void CG_DrawMediaProgress( rectDef_t *rect, vec4_t color, float scale,
                                  int align, int textStyle, int special )
{
  CG_DrawProgressBar( rect, color, scale, align, textStyle, special, cg.mediaFraction );
}

static void CG_DrawMediaProgressLabel( rectDef_t *rect, float text_x, float text_y,
                                       vec4_t color, float scale, int align )
{
  CG_DrawProgressLabel( rect, text_x, text_y, color, scale, align, "Map and Textures", cg.mediaFraction );
}

static void CG_DrawBuildablesProgress( rectDef_t *rect, vec4_t color, float scale,
                                       int align, int textStyle, int special )
{
  CG_DrawProgressBar( rect, color, scale, align, textStyle, special, cg.buildablesFraction );
}

static void CG_DrawBuildablesProgressLabel( rectDef_t *rect, float text_x, float text_y,
                                            vec4_t color, float scale, int align )
{
  CG_DrawProgressLabel( rect, text_x, text_y, color, scale, align, "Buildable Models", cg.buildablesFraction );
}

static void CG_DrawCharModelProgress( rectDef_t *rect, vec4_t color, float scale,
                                      int align, int textStyle, int special )
{
  CG_DrawProgressBar( rect, color, scale, align, textStyle, special, cg.charModelFraction );
}

static void CG_DrawCharModelProgressLabel( rectDef_t *rect, float text_x, float text_y,
                                           vec4_t color, float scale, int align )
{
  CG_DrawProgressLabel( rect, text_x, text_y, color, scale, align, "Character Models", cg.charModelFraction );
}

static void CG_DrawOverallProgress( rectDef_t *rect, vec4_t color, float scale,
                                    int align, int textStyle, int special )
{
  float total;

  total = ( cg.charModelFraction + cg.buildablesFraction + cg.mediaFraction ) / 3.0f;
  CG_DrawProgressBar( rect, color, scale, align, textStyle, special, total );
}

static void CG_DrawLevelShot( rectDef_t *rect )
{
  const char  *s;
  const char  *info;
  qhandle_t   levelshot;
  qhandle_t   detail;

  info = CG_ConfigString( CS_SERVERINFO );
  s = Info_ValueForKey( info, "mapname" );
  levelshot = trap_R_RegisterShaderNoMip( va( "levelshots/%s.tga", s ) );

  if( !levelshot )
    levelshot = trap_R_RegisterShaderNoMip( "gfx/2d/load_screen" );

  trap_R_SetColor( NULL );
  CG_DrawPic( rect->x, rect->y, rect->w, rect->h, levelshot );

  // blend a detail texture over it
  detail = trap_R_RegisterShader( "gfx/misc/detail" );
  CG_DrawPic( rect->x, rect->y, rect->w, rect->h, detail );
}

static void CG_DrawLoadingString( rectDef_t *rect, float text_x, float text_y, vec4_t color,
                                  float scale, int align, int textStyle, const char *s )
{
  float tw, th, tx;
  int   pos, i;
  char  buffer[ 1024 ];
  char  *end;

  if( !s[ 0 ] )
    return;

  strcpy( buffer, s );
  tw = CG_Text_Width( s, scale, 0 );
  th = scale * 40.0f;

  pos = i = 0;

  while( pos < strlen( s ) )
  {
    strcpy( buffer, &s[ pos ] );
    tw = CG_Text_Width( buffer, scale, 0 );

    while( tw > rect->w )
    {
      end = strrchr( buffer, ' ' );

      if( end == NULL )
        break;

      *end = '\0';
      tw = CG_Text_Width( buffer, scale, 0 );
    }

    switch( align )
    {
      case ITEM_ALIGN_LEFT:
        tx = rect->x;
        break;

      case ITEM_ALIGN_RIGHT:
        tx = rect->x + rect->w - tw;
        break;

      case ITEM_ALIGN_CENTER:
        tx = rect->x + ( rect->w / 2.0f ) - ( tw / 2.0f );
        break;

      default:
        tx = 0.0f;
    }

    CG_Text_Paint( tx + text_x, rect->y + text_y + i * ( th + 3 ), scale, color,
      buffer, 0, 0, textStyle );

    pos += strlen( buffer ) + 1;
    i++;
  }
}

static void CG_DrawLevelName( rectDef_t *rect, float text_x, float text_y,
                              vec4_t color, float scale, int align, int textStyle )
{
  const char  *s;

  s = CG_ConfigString( CS_MESSAGE );

  CG_DrawLoadingString( rect, text_x, text_y, color, scale, align, textStyle, s );
}

static void CG_DrawMOTD( rectDef_t *rect, float text_x, float text_y,
                         vec4_t color, float scale, int align, int textStyle )
{
  const char  *s;

  s = CG_ConfigString( CS_MOTD );

  CG_DrawLoadingString( rect, text_x, text_y, color, scale, align, textStyle, s );
}

static void CG_DrawHostname( rectDef_t *rect, float text_x, float text_y,
                             vec4_t color, float scale, int align, int textStyle )
{
  char buffer[ 1024 ];
  const char  *info;

  info = CG_ConfigString( CS_SERVERINFO );

  Q_strncpyz( buffer, Info_ValueForKey( info, "sv_hostname" ), 1024 );
  Q_CleanStr( buffer );

  CG_DrawLoadingString( rect, text_x, text_y, color, scale, align, textStyle, buffer );
}

/*
==============
CG_DrawDemoPlayback
==============
*/
static void CG_DrawDemoPlayback( rectDef_t *rect, vec4_t color, qhandle_t shader )
{
  if( !cg_drawDemoState.integer )
    return;

  if( trap_GetDemoState( ) != DS_PLAYBACK )
    return;

  trap_R_SetColor( color );
  CG_DrawPic( rect->x, rect->y, rect->w, rect->h, shader );
  trap_R_SetColor( NULL );
}

/*
==============
CG_DrawDemoRecording
==============
*/
static void CG_DrawDemoRecording( rectDef_t *rect, vec4_t color, qhandle_t shader )
{
  if( !cg_drawDemoState.integer )
    return;

  if( trap_GetDemoState( ) != DS_RECORDING )
    return;

  trap_R_SetColor( color );
  CG_DrawPic( rect->x, rect->y, rect->w, rect->h, shader );
  trap_R_SetColor( NULL );
}

/*
======================
CG_UpdateMediaFraction

======================
*/
void CG_UpdateMediaFraction( float newFract )
{
  cg.mediaFraction = newFract;

  trap_UpdateScreen( );
}

/*
====================
CG_DrawLoadingScreen

Draw all the status / pacifier stuff during level loading
====================
*/
void CG_DrawLoadingScreen( void )
{
  Menu_Paint( Menus_FindByName( "Loading" ), qtrue );
}

float CG_GetValue( int ownerDraw )
{
  centity_t *cent;
  playerState_t *ps;

  cent = &cg_entities[ cg.snap->ps.clientNum ];
  ps = &cg.snap->ps;

  switch( ownerDraw )
  {
    case CG_PLAYER_AMMO_VALUE:
      if( cent->currentState.weapon )
      {
        int value;

        BG_UnpackAmmoArray( cent->currentState.weapon, ps->ammo, ps->powerups,
           &value, NULL );

        return value;
      }
      break;
    case CG_PLAYER_CLIPS_VALUE:
      if( cent->currentState.weapon )
      {
        int value;

        BG_UnpackAmmoArray( cent->currentState.weapon, ps->ammo, ps->powerups,
           NULL, &value );

        return value;
      }
      break;
    case CG_PLAYER_HEALTH:
      return ps->stats[ STAT_HEALTH ];
      break;
    default:
      break;
  }

  return -1;
}

const char *CG_GetKillerText( )
{
  const char *s = "";
  if( cg.killerName[ 0 ] )
    s = va( "Fragged by %s", cg.killerName );

  return s;
}


static void CG_DrawKiller( rectDef_t *rect, float scale, vec4_t color,
                           qhandle_t shader, int textStyle )
{
  // fragged by ... line
 if( cg.killerName[ 0 ] )
 {
   int x = rect->x + rect->w / 2;
   CG_Text_Paint( x - CG_Text_Width( CG_GetKillerText( ), scale, 0 ) / 2,
     rect->y + rect->h, scale, color, CG_GetKillerText( ), 0, 0, textStyle );
  }
}


static void CG_Text_Paint_Limit( float *maxX, float x, float y, float scale,
                                 vec4_t color, const char* text, float adjust, int limit )
{
  int         len, count;
  vec4_t      newColor;
  glyphInfo_t *glyph;

  if( text )
  {
// TTimo: FIXME
//    const unsigned char *s = text; // bk001206 - unsigned
    const char *s = text;
    float max = *maxX;
    float useScale;
    fontInfo_t *font = &cgDC.Assets.textFont;

    if( scale <= cg_smallFont.value )
      font = &cgDC.Assets.smallFont;
    else if( scale > cg_bigFont.value )
      font = &cgDC.Assets.bigFont;

    useScale = scale * font->glyphScale;
    trap_R_SetColor( color );
    len = strlen( text );

    if( limit > 0 && len > limit )
      len = limit;

    count = 0;

    while( s && *s && count < len )
    {
      glyph = &font->glyphs[ (int)*s ];
      //TTimo: FIXME: getting nasty warnings without the cast,
      //hopefully this doesn't break the VM build

      if( Q_IsColorString( s ) )
      {
        memcpy( newColor, g_color_table[ ColorIndex( *(s+1) ) ], sizeof( newColor ) );
        newColor[ 3 ] = color[ 3 ];
        trap_R_SetColor( newColor );
        s += 2;
        continue;
      }
      else
      {
        float yadj = useScale * glyph->top;

        if( CG_Text_Width( s, useScale, 1 ) + x > max )
        {
          *maxX = 0;
          break;
        }

        CG_Text_PaintChar( x, y - yadj,
                           glyph->imageWidth,
                           glyph->imageHeight,
                           useScale,
                           glyph->s,
                           glyph->t,
                           glyph->s2,
                           glyph->t2,
                           glyph->glyph );
        x += ( glyph->xSkip * useScale ) + adjust;
        *maxX = x;
        count++;
        s++;
      }
    }

    trap_R_SetColor( NULL );
  }
}

static void CG_DrawTeamSpectators( rectDef_t *rect, float scale, vec4_t color, qhandle_t shader )
{
  if( cg.spectatorLen )
  {
    float maxX;

    if( cg.spectatorWidth == -1 )
    {
      cg.spectatorWidth = 0;
      cg.spectatorPaintX = rect->x + 1;
      cg.spectatorPaintX2 = -1;
    }

    if( cg.spectatorOffset > cg.spectatorLen )
    {
      cg.spectatorOffset = 0;
      cg.spectatorPaintX = rect->x + 1;
      cg.spectatorPaintX2 = -1;
    }

    if( cg.time > cg.spectatorTime )
    {
      cg.spectatorTime = cg.time + 10;

      if( cg.spectatorPaintX <= rect->x + 2 )
      {
        if( cg.spectatorOffset < cg.spectatorLen )
        {
          //TA: skip colour directives
          if( Q_IsColorString( &cg.spectatorList[ cg.spectatorOffset ] ) )
            cg.spectatorOffset += 2;
          else
          {
            cg.spectatorPaintX += CG_Text_Width( &cg.spectatorList[ cg.spectatorOffset ], scale, 1 ) - 1;
            cg.spectatorOffset++;
          }
        }
        else
        {
          cg.spectatorOffset = 0;

          if( cg.spectatorPaintX2 >= 0 )
            cg.spectatorPaintX = cg.spectatorPaintX2;
          else
            cg.spectatorPaintX = rect->x + rect->w - 2;

          cg.spectatorPaintX2 = -1;
        }
      }
      else
      {
        cg.spectatorPaintX--;

        if( cg.spectatorPaintX2 >= 0 )
          cg.spectatorPaintX2--;
      }
    }

    maxX = rect->x + rect->w - 2;

    CG_Text_Paint_Limit( &maxX, cg.spectatorPaintX, rect->y + rect->h - 3, scale, color,
                         &cg.spectatorList[ cg.spectatorOffset ], 0, 0 );

    if( cg.spectatorPaintX2 >= 0 )
    {
      float maxX2 = rect->x + rect->w - 2;
      CG_Text_Paint_Limit( &maxX2, cg.spectatorPaintX2, rect->y + rect->h - 3, scale,
                           color, cg.spectatorList, 0, cg.spectatorOffset );
    }

    if( cg.spectatorOffset && maxX > 0 )
    {
      // if we have an offset ( we are skipping the first part of the string ) and we fit the string
      if( cg.spectatorPaintX2 == -1 )
        cg.spectatorPaintX2 = rect->x + rect->w - 2;
    }
    else
      cg.spectatorPaintX2 = -1;
  }
}

/*
==================
CG_DrawStageReport
==================
*/
static void CG_DrawStageReport( rectDef_t *rect, float text_x, float text_y,
    vec4_t color, float scale, int align, int textStyle )
{
  char  s[ MAX_TOKEN_CHARS ];
  int   tx, w, kills;

  if( cg.snap->ps.stats[ STAT_PTEAM ] == PTE_NONE && !cg.intermissionStarted )
    return;

  if( cg.intermissionStarted )
  {
    Com_sprintf( s, MAX_TOKEN_CHARS,
        "Stage %d" //PH34R MY MAD-LEET CODING SKILLZ
        "                                                       "
        "Stage %d",
        cgs.alienStage + 1, cgs.humanStage + 1 );
  }
  else if( cg.snap->ps.stats[ STAT_PTEAM ] == PTE_ALIENS )
  {
    kills = cgs.alienNextStageThreshold - cgs.alienKills;

    if( cgs.alienNextStageThreshold < 0 )
      Com_sprintf( s, MAX_TOKEN_CHARS, "Stage %d", cgs.alienStage + 1 );
    else if( kills == 1 )
      Com_sprintf( s, MAX_TOKEN_CHARS, "Stage %d, %d kill for next stage",
          cgs.alienStage + 1, kills );
    else
      Com_sprintf( s, MAX_TOKEN_CHARS, "Stage %d, %d kills for next stage",
          cgs.alienStage + 1, kills );
  }
  else if( cg.snap->ps.stats[ STAT_PTEAM ] == PTE_HUMANS )
  {
    kills = cgs.humanNextStageThreshold - cgs.humanKills;

    if( cgs.humanNextStageThreshold < 0 )
      Com_sprintf( s, MAX_TOKEN_CHARS, "Stage %d", cgs.humanStage + 1 );
    else if( kills == 1 )
      Com_sprintf( s, MAX_TOKEN_CHARS, "Stage %d, %d kill for next stage",
          cgs.humanStage + 1, kills );
    else
      Com_sprintf( s, MAX_TOKEN_CHARS, "Stage %d, %d kills for next stage",
          cgs.humanStage + 1, kills );
  }

  w = CG_Text_Width( s, scale, 0 );

  switch( align )
  {
    case ITEM_ALIGN_LEFT:
      tx = rect->x;
      break;

    case ITEM_ALIGN_RIGHT:
      tx = rect->x + rect->w - w;
      break;

    case ITEM_ALIGN_CENTER:
      tx = rect->x + ( rect->w / 2.0f ) - ( w / 2.0f );
      break;

    default:
      tx = 0.0f;
  }

  CG_Text_Paint( text_x + tx, rect->y + text_y, scale, color, s, 0, 0, textStyle );
}

/*
==================
CG_DrawFPS
==================
*/
//TA: personally i think this should be longer - it should really be a cvar
#define FPS_FRAMES  20
#define FPS_STRING  "fps"
static void CG_DrawFPS( rectDef_t *rect, float text_x, float text_y,
                        float scale, vec4_t color, int align, int textStyle,
                        qboolean scalableText )
{
  char        *s;
  int         tx, w, totalWidth, strLength;
  static int  previousTimes[ FPS_FRAMES ];
  static int  index;
  int         i, total;
  int         fps;
  static int  previous;
  int         t, frameTime;

  if( !cg_drawFPS.integer )
    return;

  // don't use serverTime, because that will be drifting to
  // correct for internet lag changes, timescales, timedemos, etc
  t = trap_Milliseconds( );
  frameTime = t - previous;
  previous = t;

  previousTimes[ index % FPS_FRAMES ] = frameTime;
  index++;

  if( index > FPS_FRAMES )
  {
    // average multiple frames together to smooth changes out a bit
    total = 0;

    for( i = 0 ; i < FPS_FRAMES ; i++ )
      total += previousTimes[ i ];

    if( !total )
      total = 1;

    fps = 1000 * FPS_FRAMES / total;

    s = va( "%d", fps );
    w = CG_Text_Width( "0", scale, 0 );
    strLength = CG_DrawStrlen( s );
    totalWidth = CG_Text_Width( FPS_STRING, scale, 0 ) + w * strLength;

    switch( align )
    {
      case ITEM_ALIGN_LEFT:
        tx = rect->x;
        break;

      case ITEM_ALIGN_RIGHT:
        tx = rect->x + rect->w - totalWidth;
        break;

      case ITEM_ALIGN_CENTER:
        tx = rect->x + ( rect->w / 2.0f ) - ( totalWidth / 2.0f );
        break;

      default:
        tx = 0.0f;
    }

    if( scalableText )
    {
      for( i = 0; i < strLength; i++ )
      {
        char c[ 2 ];

        c[ 0 ] = s[ i ];
        c[ 1 ] = '\0';

        CG_Text_Paint( text_x + tx + i * w, rect->y + text_y, scale, color, c, 0, 0, textStyle );
      }
    }
    else
    {
      trap_R_SetColor( color );
      CG_DrawField( rect->x, rect->y, 3, rect->w / 3, rect->h, fps );
      trap_R_SetColor( NULL );
    }

    if( scalableText )
      CG_Text_Paint( text_x + tx + i * w, rect->y + text_y, scale, color, FPS_STRING, 0, 0, textStyle );
  }
}


/*
=================
CG_DrawTimerMins
=================
*/
static void CG_DrawTimerMins( rectDef_t *rect, vec4_t color )
{
  int     mins, seconds;
  int     msec;

  if( !cg_drawTimer.integer )
    return;

  msec = cg.time - cgs.levelStartTime;

  seconds = msec / 1000;
  mins = seconds / 60;
  seconds -= mins * 60;

  trap_R_SetColor( color );
  CG_DrawField( rect->x, rect->y, 3, rect->w / 3, rect->h, mins );
  trap_R_SetColor( NULL );
}


/*
=================
CG_DrawTimerSecs
=================
*/
static void CG_DrawTimerSecs( rectDef_t *rect, vec4_t color )
{
  int     mins, seconds;
  int     msec;

  if( !cg_drawTimer.integer )
    return;

  msec = cg.time - cgs.levelStartTime;

  seconds = msec / 1000;
  mins = seconds / 60;
  seconds -= mins * 60;

  trap_R_SetColor( color );
  CG_DrawFieldPadded( rect->x, rect->y, 2, rect->w / 2, rect->h, seconds );
  trap_R_SetColor( NULL );
}


/*
=================
CG_DrawTimer
=================
*/
static void CG_DrawTimer( rectDef_t *rect, float text_x, float text_y,
                          float scale, vec4_t color, int align, int textStyle )
{
  char    *s;
  int     i, tx, w, totalWidth, strLength;
  int     mins, seconds, tens;
  int     msec;

  if( !cg_drawTimer.integer )
    return;

  msec = cg.time - cgs.levelStartTime;

  seconds = msec / 1000;
  mins = seconds / 60;
  seconds -= mins * 60;
  tens = seconds / 10;
  seconds -= tens * 10;

  s = va( "%d:%d%d", mins, tens, seconds );
  w = CG_Text_Width( "0", scale, 0 );
  strLength = CG_DrawStrlen( s );
  totalWidth = w * strLength;

  switch( align )
  {
    case ITEM_ALIGN_LEFT:
      tx = rect->x;
      break;

    case ITEM_ALIGN_RIGHT:
      tx = rect->x + rect->w - totalWidth;
      break;

    case ITEM_ALIGN_CENTER:
      tx = rect->x + ( rect->w / 2.0f ) - ( totalWidth / 2.0f );
      break;

    default:
      tx = 0.0f;
  }

  for( i = 0; i < strLength; i++ )
  {
    char c[ 2 ];

    c[ 0 ] = s[ i ];
    c[ 1 ] = '\0';

    CG_Text_Paint( text_x + tx + i * w, rect->y + text_y, scale, color, c, 0, 0, textStyle );
  }
}

/*
=================
CG_DrawClock
=================
*/
static void CG_DrawClock( rectDef_t *rect, float text_x, float text_y,
                          float scale, vec4_t color, int align, int textStyle )
{
  char    *s;
  int     i, tx, w, totalWidth, strLength;
  qtime_t qt;
  int     t;

  if( !cg_drawClock.integer )
    return;

  t = trap_RealTime( &qt );

  if( cg_drawClock.integer == 2 )
  {
    s = va( "%02d%s%02d", qt.tm_hour, ( qt.tm_sec % 2 ) ? ":" : " ",
      qt.tm_min );
  }
  else
  {
    char *pm = "am";
    int h = qt.tm_hour;

    if( h == 0 )
      h = 12;
    else if( h == 12 )
      pm = "pm";
    else if( h > 12 )
    {
      h -= 12;
      pm = "pm";
    }

    s = va( "%d%s%02d%s", h, ( qt.tm_sec % 2 ) ? ":" : " ", qt.tm_min, pm );
  }
  w = CG_Text_Width( "0", scale, 0 );
  strLength = CG_DrawStrlen( s );
  totalWidth = w * strLength;

  switch( align )
  {
    case ITEM_ALIGN_LEFT:
      tx = rect->x;
      break;

    case ITEM_ALIGN_RIGHT:
      tx = rect->x + rect->w - totalWidth;
      break;

    case ITEM_ALIGN_CENTER:
      tx = rect->x + ( rect->w / 2.0f ) - ( totalWidth / 2.0f );
      break;

    default:
      tx = 0.0f;
  }

  for( i = 0; i < strLength; i++ )
  {
    char c[ 2 ];

    c[ 0 ] = s[ i ];
    c[ 1 ] = '\0';

    CG_Text_Paint( text_x + tx + i * w, rect->y + text_y, scale, color, c, 0, 0, textStyle );
  }
}

/*
==================
CG_DrawSnapshot
==================
*/
static void CG_DrawSnapshot( rectDef_t *rect, float text_x, float text_y,
                             float scale, vec4_t color, int align, int textStyle )
{
  char    *s;
  int     w, tx;

  if( !cg_drawSnapshot.integer )
    return;

  s = va( "time:%d snap:%d cmd:%d", cg.snap->serverTime,
    cg.latestSnapshotNum, cgs.serverCommandSequence );
  w = CG_Text_Width( s, scale, 0 );

  switch( align )
  {
    case ITEM_ALIGN_LEFT:
      tx = rect->x;
      break;

    case ITEM_ALIGN_RIGHT:
      tx = rect->x + rect->w - w;
      break;

    case ITEM_ALIGN_CENTER:
      tx = rect->x + ( rect->w / 2.0f ) - ( w / 2.0f );
      break;

    default:
      tx = 0.0f;
  }

  CG_Text_Paint( text_x + tx, rect->y + text_y, scale, color, s, 0, 0, textStyle );
}

/*
===============================================================================

LAGOMETER

===============================================================================
*/

#define LAG_SAMPLES   128

typedef struct
{
  int frameSamples[ LAG_SAMPLES ];
  int frameCount;
  int snapshotFlags[ LAG_SAMPLES ];
  int snapshotSamples[ LAG_SAMPLES ];
  int snapshotCount;
} lagometer_t;

lagometer_t   lagometer;

/*
==============
CG_AddLagometerFrameInfo

Adds the current interpolate / extrapolate bar for this frame
==============
*/
void CG_AddLagometerFrameInfo( void )
{
  int     offset;

  offset = cg.time - cg.latestSnapshotTime;
  lagometer.frameSamples[ lagometer.frameCount & ( LAG_SAMPLES - 1 ) ] = offset;
  lagometer.frameCount++;
}

/*
==============
CG_AddLagometerSnapshotInfo

Each time a snapshot is received, log its ping time and
the number of snapshots that were dropped before it.

Pass NULL for a dropped packet.
==============
*/
#define PING_FRAMES 40
void CG_AddLagometerSnapshotInfo( snapshot_t *snap )
{
  static int  previousPings[ PING_FRAMES ];
  static int  index;
  int         i;

  // dropped packet
  if( !snap )
  {
    lagometer.snapshotSamples[ lagometer.snapshotCount & ( LAG_SAMPLES - 1 ) ] = -1;
    lagometer.snapshotCount++;
    return;
  }

  // add this snapshot's info
  lagometer.snapshotSamples[ lagometer.snapshotCount & ( LAG_SAMPLES - 1 ) ] = snap->ping;
  lagometer.snapshotFlags[ lagometer.snapshotCount & ( LAG_SAMPLES - 1 ) ] = snap->snapFlags;
  lagometer.snapshotCount++;

  cg.ping = 0;
  if( cg.snap )
  {
    previousPings[ index++ ] = cg.snap->ping;
    index = index % PING_FRAMES;
  
    for( i = 0; i < PING_FRAMES; i++ )
    {
      cg.ping += previousPings[ i ];
    }

    cg.ping /= PING_FRAMES;
  }
}

/*
==============
CG_DrawDisconnect

Should we draw something differnet for long lag vs no packets?
==============
*/
static void CG_DrawDisconnect( void )
{
  float       x, y;
  int         cmdNum;
  usercmd_t   cmd;
  const char  *s;
  int         w;
  vec4_t      color = { 1.0f, 1.0f, 1.0f, 1.0f };

  // draw the phone jack if we are completely past our buffers
  cmdNum = trap_GetCurrentCmdNumber( ) - CMD_BACKUP + 1;
  trap_GetUserCmd( cmdNum, &cmd );

  // special check for map_restart
  if( cmd.serverTime <= cg.snap->ps.commandTime || cmd.serverTime > cg.time )
    return;

  // also add text in center of screen
  s = "Connection Interrupted";
  w = CG_Text_Width( s, 0.7f, 0 );
  CG_Text_Paint( 320 - w / 2, 100, 0.7f, color, s, 0, 0, ITEM_TEXTSTYLE_SHADOWED );

  // blink the icon
  if( ( cg.time >> 9 ) & 1 )
    return;

  x = 640 - 48;
  y = 480 - 48;

  CG_DrawPic( x, y, 48, 48, trap_R_RegisterShader( "gfx/2d/net.tga" ) );
}

#define MAX_LAGOMETER_PING  900
#define MAX_LAGOMETER_RANGE 300


/*
==============
CG_DrawLagometer
==============
*/
static void CG_DrawLagometer( rectDef_t *rect, float text_x, float text_y,
    float scale, vec4_t textColor )
{
  int     a, x, y, i;
  float   v;
  float   ax, ay, aw, ah, mid, range;
  int     color;
  vec4_t  adjustedColor;
  float   vscale;
  vec4_t  white = { 1.0f, 1.0f, 1.0f, 1.0f };

  if( cg.snap->ps.pm_type == PM_INTERMISSION )
    return;

  if( !cg_lagometer.integer )
    return;

  if( cg.demoPlayback )
    return;

  Vector4Copy( textColor, adjustedColor );
  adjustedColor[ 3 ] = 0.25f;

  trap_R_SetColor( adjustedColor );
  CG_DrawPic( rect->x, rect->y, rect->w, rect->h, cgs.media.whiteShader );
  trap_R_SetColor( NULL );

  //
  // draw the graph
  //
  ax = x = rect->x;
  ay = y = rect->y;
  aw = rect->w;
  ah = rect->h;

  trap_R_SetColor( NULL );

  CG_AdjustFrom640( &ax, &ay, &aw, &ah );

  color = -1;
  range = ah / 3;
  mid = ay + range;

  vscale = range / MAX_LAGOMETER_RANGE;

  // draw the frame interpoalte / extrapolate graph
  for( a = 0 ; a < aw ; a++ )
  {
    i = ( lagometer.frameCount - 1 - a ) & ( LAG_SAMPLES - 1 );
    v = lagometer.frameSamples[ i ];
    v *= vscale;

    if( v > 0 )
    {
      if( color != 1 )
      {
        color = 1;
        trap_R_SetColor( g_color_table[ ColorIndex( COLOR_YELLOW ) ] );
      }

      if( v > range )
        v = range;

      trap_R_DrawStretchPic( ax + aw - a, mid - v, 1, v, 0, 0, 0, 0, cgs.media.whiteShader );
    }
    else if( v < 0 )
    {
      if( color != 2 )
      {
        color = 2;
        trap_R_SetColor( g_color_table[ ColorIndex( COLOR_BLUE ) ] );
      }

      v = -v;
      if( v > range )
        v = range;

      trap_R_DrawStretchPic( ax + aw - a, mid, 1, v, 0, 0, 0, 0, cgs.media.whiteShader );
    }
  }

  // draw the snapshot latency / drop graph
  range = ah / 2;
  vscale = range / MAX_LAGOMETER_PING;

  for( a = 0 ; a < aw ; a++ )
  {
    i = ( lagometer.snapshotCount - 1 - a ) & ( LAG_SAMPLES - 1 );
    v = lagometer.snapshotSamples[ i ];

    if( v > 0 )
    {
      if( lagometer.snapshotFlags[ i ] & SNAPFLAG_RATE_DELAYED )
      {
        if( color != 5 )
        {
          color = 5;  // YELLOW for rate delay
          trap_R_SetColor( g_color_table[ ColorIndex( COLOR_YELLOW ) ] );
        }
      }
      else
      {
        if( color != 3 )
        {
          color = 3;

          trap_R_SetColor( g_color_table[ ColorIndex( COLOR_GREEN ) ] );
        }
      }

      v = v * vscale;

      if( v > range )
        v = range;

      trap_R_DrawStretchPic( ax + aw - a, ay + ah - v, 1, v, 0, 0, 0, 0, cgs.media.whiteShader );
    }
    else if( v < 0 )
    {
      if( color != 4 )
      {
        color = 4;    // RED for dropped snapshots
        trap_R_SetColor( g_color_table[ ColorIndex( COLOR_RED ) ] );
      }

      trap_R_DrawStretchPic( ax + aw - a, ay + ah - range, 1, range, 0, 0, 0, 0, cgs.media.whiteShader );
    }
  }

  trap_R_SetColor( NULL );

  if( cg_nopredict.integer || cg_synchronousClients.integer )
    CG_Text_Paint( ax, ay, 0.5, white, "snc", 0, 0, ITEM_TEXTSTYLE_NORMAL );
  else
  {
    char        *s;

    s = va( "%d", cg.ping );
    ax = rect->x + ( rect->w / 2.0f ) - ( CG_Text_Width( s, scale, 0 ) / 2.0f ) + text_x;
    ay = rect->y + ( rect->h / 2.0f ) + ( CG_Text_Height( s, scale, 0 ) / 2.0f ) + text_y;

    Vector4Copy( textColor, adjustedColor );
    adjustedColor[ 3 ] = 0.5f;
    CG_Text_Paint( ax, ay, scale, adjustedColor, s, 0, 0, ITEM_TEXTSTYLE_NORMAL );
  }

  CG_DrawDisconnect( );
}

/*
==============
CG_DrawTextBlock
==============
*/
static void CG_DrawTextBlock( rectDef_t *rect, float text_x, float text_y, vec4_t color,
                            float scale, int align, int textStyle, const char *text,
                            menuDef_t *parent, itemDef_t *textItem )
{
  float     x, y, w, h;

  //offset the text
  x = rect->x;
  y = rect->y;
  w = rect->w - ( 16 + ( 2 * text_x ) ); //16 to ensure text within frame
  h = rect->h;

  textItem->text = text;

  textItem->parent = parent;
  memcpy( textItem->window.foreColor, color, sizeof( vec4_t ) );
  textItem->window.flags = 0;

  switch( align )
  {
    case ITEM_ALIGN_LEFT:
      textItem->window.rect.x = x;
      break;

    case ITEM_ALIGN_RIGHT:
      textItem->window.rect.x = x + w;
      break;

    case ITEM_ALIGN_CENTER:
      textItem->window.rect.x = x + ( w / 2 );
      break;

    default:
      textItem->window.rect.x = x;
      break;
  }

  textItem->window.rect.y = y;
  textItem->window.rect.w = w;
  textItem->window.rect.h = h;
  textItem->window.borderSize = 0;
  textItem->textRect.x = 0;
  textItem->textRect.y = 0;
  textItem->textRect.w = 0;
  textItem->textRect.h = 0;
  textItem->textalignment = align;
  textItem->textalignx = text_x;
  textItem->textaligny = text_y;
  textItem->textscale = scale;
  textItem->textStyle = textStyle;

  //hack to utilise existing autowrap code
  Item_Text_AutoWrapped_Paint( textItem );
}

/*
===================
CG_DrawConsole
===================
*/
static void CG_DrawConsole( rectDef_t *rect, float text_x, float text_y, vec4_t color,
                            float scale, int align, int textStyle )
{
  static menuDef_t dummyParent;
  static itemDef_t textItem;

  CG_DrawTextBlock( rect, text_x, text_y, color, scale, align, textStyle,
      cg.consoleText, &dummyParent, &textItem );
}

/*
===================
CG_DrawTutorial
===================
*/
static void CG_DrawTutorial( rectDef_t *rect, float text_x, float text_y, vec4_t color,
                            float scale, int align, int textStyle )
{
  static menuDef_t dummyParent;
  static itemDef_t textItem;

  if( !cg_tutorial.integer )
    return;

  CG_DrawTextBlock( rect, text_x, text_y, color, scale, align, textStyle,
      CG_TutorialText( ), &dummyParent, &textItem );
}

/*
===================
CG_DrawWeaponIcon
===================
*/
void CG_DrawWeaponIcon( rectDef_t *rect, vec4_t color )
{
  int           ammo, clips, maxAmmo;
  centity_t     *cent;
  playerState_t *ps;

  cent = &cg_entities[ cg.snap->ps.clientNum ];
  ps = &cg.snap->ps;

  BG_UnpackAmmoArray( cent->currentState.weapon, ps->ammo, ps->powerups, &ammo, &clips );
  BG_FindAmmoForWeapon( cent->currentState.weapon, &maxAmmo, NULL );

  // don't display if dead
  if( cg.predictedPlayerState.stats[ STAT_HEALTH ] <= 0 )
    return;

  if( cent->currentState.weapon == 0 )
    return;

  CG_RegisterWeapon( cent->currentState.weapon );

  if( clips == 0 && !BG_FindInfinteAmmoForWeapon( cent->currentState.weapon ) )
  {
    float ammoPercent = (float)ammo / (float)maxAmmo;

    if( ammoPercent < 0.33f )
    {
      color[ 0 ] = 1.0f;
      color[ 1 ] = color[ 2 ] = 0.0f;
    }
  }

  if( cg.predictedPlayerState.stats[ STAT_PTEAM ] == PTE_ALIENS && CG_AtHighestClass( ) )
  {
    if( cg.time - cg.lastEvolveAttempt <= NO_CREDITS_TIME )
    {
      if( ( ( cg.time - cg.lastEvolveAttempt ) / 300 ) % 2 )
        color[ 3 ] = 0.0f;
    }
  }

  trap_R_SetColor( color );
  CG_DrawPic( rect->x, rect->y, rect->w, rect->h, cg_weapons[ cent->currentState.weapon ].weaponIcon );
  trap_R_SetColor( NULL );
}



/*
================================================================================

CROSSHAIR

================================================================================
*/


/*
=================
CG_DrawCrosshair
=================
*/
static void CG_DrawCrosshair( void )
{
  float         w, h;
  qhandle_t     hShader;
  float         x, y;
  weaponInfo_t  *wi;

  if( cg_drawCrosshair.integer == CROSSHAIR_ALWAYSOFF )
    return;

  if( cg_drawCrosshair.integer == CROSSHAIR_RANGEDONLY &&
      !BG_FindLongRangedForWeapon( cg.snap->ps.weapon ) )
  {
    return;
  } 

  if( ( cg.snap->ps.persistant[ PERS_TEAM ] == TEAM_SPECTATOR ) ||
      ( cg.snap->ps.stats[ STAT_STATE ] & SS_INFESTING ) ||
      ( cg.snap->ps.stats[ STAT_STATE ] & SS_HOVELING ) )
    return;

  if( cg.renderingThirdPerson )
    return;

  wi = &cg_weapons[ cg.snap->ps.weapon ];

  w = h = wi->crossHairSize;

  x = cg_crosshairX.integer;
  y = cg_crosshairY.integer;
  CG_AdjustFrom640( &x, &y, &w, &h );

  hShader = wi->crossHair;

  if( hShader != 0 )
  {
    trap_R_DrawStretchPic( x + cg.refdef.x + 0.5 * ( cg.refdef.width - w ),
      y + cg.refdef.y + 0.5 * ( cg.refdef.height - h ),
      w, h, 0, 0, 1, 1, hShader );
  }
}



/*
=================
CG_ScanForCrosshairEntity
=================
*/
static void CG_ScanForCrosshairEntity( void )
{
  trace_t   trace;
  vec3_t    start, end;
  int       content;
  pTeam_t   team;

  VectorCopy( cg.refdef.vieworg, start );
  VectorMA( start, 131072, cg.refdef.viewaxis[ 0 ], end );

  CG_Trace( &trace, start, vec3_origin, vec3_origin, end,
    cg.snap->ps.clientNum, CONTENTS_SOLID|CONTENTS_BODY );

  if( trace.entityNum >= MAX_CLIENTS )
    return;

  // if the player is in fog, don't show it
  content = trap_CM_PointContents( trace.endpos, 0 );
  if( content & CONTENTS_FOG )
    return;

  team = cgs.clientinfo[ trace.entityNum ].team;

  if( cg.snap->ps.persistant[ PERS_TEAM ] != TEAM_SPECTATOR )
  {
    //only display team names of those on the same team as this player
    if( team != cg.snap->ps.stats[ STAT_PTEAM ] )
      return;
  }

  // update the fade timer
  cg.crosshairClientNum = trace.entityNum;
  cg.crosshairClientTime = cg.time;
}


/*
=====================
CG_DrawCrosshairNames
=====================
*/
static void CG_DrawCrosshairNames( rectDef_t *rect, float scale, int textStyle )
{
  float   *color;
  char    *name;
  float   w, x;

  if( !cg_drawCrosshairNames.integer )
    return;

  if( cg.renderingThirdPerson )
    return;

  // scan the known entities to see if the crosshair is sighted on one
  CG_ScanForCrosshairEntity( );

  // draw the name of the player being looked at
  color = CG_FadeColor( cg.crosshairClientTime, 1000 );
  if( !color )
  {
    trap_R_SetColor( NULL );
    return;
  }

  name = cgs.clientinfo[ cg.crosshairClientNum ].name;
  w = CG_Text_Width( name, scale, 0 );
  x = rect->x + rect->w / 2;
  CG_Text_Paint( x - w / 2, rect->y + rect->h, scale, color, name, 0, 0, textStyle );
  trap_R_SetColor( NULL );
}


/*
===============
CG_OwnerDraw

Draw an owner drawn item
===============
*/
void CG_OwnerDraw( float x, float y, float w, float h, float text_x,
                   float text_y, int ownerDraw, int ownerDrawFlags,
                   int align, float special, float scale, vec4_t color,
                   qhandle_t shader, int textStyle )
{
  rectDef_t rect;

  if( cg_drawStatus.integer == 0 )
    return;

  rect.x = x;
  rect.y = y;
  rect.w = w;
  rect.h = h;

  switch( ownerDraw )
  {
    case CG_PLAYER_CREDITS_VALUE:
      CG_DrawPlayerCreditsValue( &rect, color, qtrue );
      break;
    case CG_PLAYER_BANK_VALUE:
      CG_DrawPlayerBankValue( &rect, color, qtrue );
      break;
    case CG_PLAYER_CREDITS_VALUE_NOPAD:
      CG_DrawPlayerCreditsValue( &rect, color, qfalse );
      break;
    case CG_PLAYER_BANK_VALUE_NOPAD:
      CG_DrawPlayerBankValue( &rect, color, qfalse );
      break;
    case CG_PLAYER_STAMINA:
      CG_DrawPlayerStamina( &rect, color, scale, align, textStyle, special );
      break;
    case CG_PLAYER_STAMINA_1:
      CG_DrawPlayerStamina1( &rect, color, shader );
      break;
    case CG_PLAYER_STAMINA_2:
      CG_DrawPlayerStamina2( &rect, color, shader );
      break;
    case CG_PLAYER_STAMINA_3:
      CG_DrawPlayerStamina3( &rect, color, shader );
      break;
    case CG_PLAYER_STAMINA_4:
      CG_DrawPlayerStamina4( &rect, color, shader );
      break;
    case CG_PLAYER_STAMINA_BOLT:
      CG_DrawPlayerStaminaBolt( &rect, color, shader );
      break;
    case CG_PLAYER_AMMO_VALUE:
      CG_DrawPlayerAmmoValue( &rect, color );
      break;
    case CG_PLAYER_CLIPS_VALUE:
      CG_DrawPlayerClipsValue( &rect, color );
      break;
    case CG_PLAYER_BUILD_TIMER:
      CG_DrawPlayerBuildTimer( &rect, color );
      break;
    case CG_PLAYER_HEALTH:
      CG_DrawPlayerHealthValue( &rect, color );
      break;
    case CG_PLAYER_HEALTH_BAR:
      CG_DrawPlayerHealthBar( &rect, color, scale, align, textStyle, special );
      break;
    case CG_PLAYER_HEALTH_CROSS:
      CG_DrawPlayerHealthCross( &rect, color, shader );
      break;
    case CG_PLAYER_CLIPS_RING:
      CG_DrawPlayerClipsRing( &rect, color, shader );
      break;
    case CG_PLAYER_BUILD_TIMER_RING:
      CG_DrawPlayerBuildTimerRing( &rect, color, shader );
      break;
    case CG_PLAYER_WALLCLIMBING:
      CG_DrawPlayerWallclimbing( &rect, color, shader );
      break;
    case CG_PLAYER_BOOSTED:
      CG_DrawPlayerBoosted( &rect, color, shader );
      break;
    case CG_PLAYER_BOOST_BOLT:
      CG_DrawPlayerBoosterBolt( &rect, color, shader );
      break;
    case CG_PLAYER_POISON_BARBS:
      CG_DrawPlayerPoisonBarbs( &rect, color, shader );
      break;
    case CG_PLAYER_ALIEN_SENSE:
      CG_DrawAlienSense( &rect );
      break;
    case CG_PLAYER_HUMAN_SCANNER:
      CG_DrawHumanScanner( &rect, shader, color );
      break;
    case CG_PLAYER_USABLE_BUILDABLE:
      CG_DrawUsableBuildable( &rect, shader, color );
      break;
    case CG_KILLER:
      CG_DrawKiller( &rect, scale, color, shader, textStyle );
      break;
    case CG_PLAYER_SELECT:
      CG_DrawItemSelect( &rect, color );
      break;
    case CG_PLAYER_WEAPONICON:
      CG_DrawWeaponIcon( &rect, color );
      break;
    case CG_PLAYER_SELECTTEXT:
      CG_DrawItemSelectText( &rect, scale, textStyle );
      break;
    case CG_SPECTATORS:
      CG_DrawTeamSpectators( &rect, scale, color, shader );
      break;
    case CG_PLAYER_CROSSHAIRNAMES:
      CG_DrawCrosshairNames( &rect, scale, textStyle );
      break;
    case CG_STAGE_REPORT_TEXT:
      CG_DrawStageReport( &rect, text_x, text_y, color, scale, align, textStyle );
      break;

    //loading screen
    case CG_LOAD_LEVELSHOT:
      CG_DrawLevelShot( &rect );
      break;
    case CG_LOAD_MEDIA:
      CG_DrawMediaProgress( &rect, color, scale, align, textStyle, special );
      break;
    case CG_LOAD_MEDIA_LABEL:
      CG_DrawMediaProgressLabel( &rect, text_x, text_y, color, scale, align );
      break;
    case CG_LOAD_BUILDABLES:
      CG_DrawBuildablesProgress( &rect, color, scale, align, textStyle, special );
      break;
    case CG_LOAD_BUILDABLES_LABEL:
      CG_DrawBuildablesProgressLabel( &rect, text_x, text_y, color, scale, align );
      break;
    case CG_LOAD_CHARMODEL:
      CG_DrawCharModelProgress( &rect, color, scale, align, textStyle, special );
      break;
    case CG_LOAD_CHARMODEL_LABEL:
      CG_DrawCharModelProgressLabel( &rect, text_x, text_y, color, scale, align );
      break;
    case CG_LOAD_OVERALL:
      CG_DrawOverallProgress( &rect, color, scale, align, textStyle, special );
      break;
    case CG_LOAD_LEVELNAME:
      CG_DrawLevelName( &rect, text_x, text_y, color, scale, align, textStyle );
      break;
    case CG_LOAD_MOTD:
      CG_DrawMOTD( &rect, text_x, text_y, color, scale, align, textStyle );
      break;
    case CG_LOAD_HOSTNAME:
      CG_DrawHostname( &rect, text_x, text_y, color, scale, align, textStyle );
      break;

    case CG_FPS:
      CG_DrawFPS( &rect, text_x, text_y, scale, color, align, textStyle, qtrue );
      break;
    case CG_FPS_FIXED:
      CG_DrawFPS( &rect, text_x, text_y, scale, color, align, textStyle, qfalse );
      break;
    case CG_TIMER:
      CG_DrawTimer( &rect, text_x, text_y, scale, color, align, textStyle );
      break;
    case CG_CLOCK:
      CG_DrawClock( &rect, text_x, text_y, scale, color, align, textStyle );
      break;
    case CG_TIMER_MINS:
      CG_DrawTimerMins( &rect, color );
      break;
    case CG_TIMER_SECS:
      CG_DrawTimerSecs( &rect, color );
      break;
    case CG_SNAPSHOT:
      CG_DrawSnapshot( &rect, text_x, text_y, scale, color, align, textStyle );
      break;
    case CG_LAGOMETER:
      CG_DrawLagometer( &rect, text_x, text_y, scale, color );
      break;

    case CG_DEMO_PLAYBACK:
      CG_DrawDemoPlayback( &rect, color, shader );
      break;
    case CG_DEMO_RECORDING:
      CG_DrawDemoRecording( &rect, color, shader );
      break;

    case CG_CONSOLE:
      CG_DrawConsole( &rect, text_x, text_y, color, scale, align, textStyle );
      break;

    case CG_TUTORIAL:
      CG_DrawTutorial( &rect, text_x, text_y, color, scale, align, textStyle );
      break;

    default:
      break;
  }
}

void CG_MouseEvent( int x, int y )
{
  int n;

  if( ( cg.predictedPlayerState.pm_type == PM_NORMAL ||
        cg.predictedPlayerState.pm_type == PM_SPECTATOR ) &&
        cg.showScores == qfalse )
  {
    trap_Key_SetCatcher( 0 );
    return;
  }

  cgs.cursorX += x;
  if( cgs.cursorX < 0 )
    cgs.cursorX = 0;
  else if( cgs.cursorX > 640 )
    cgs.cursorX = 640;

  cgs.cursorY += y;
  if( cgs.cursorY < 0 )
    cgs.cursorY = 0;
  else if( cgs.cursorY > 480 )
    cgs.cursorY = 480;

  n = Display_CursorType( cgs.cursorX, cgs.cursorY );
  cgs.activeCursor = 0;
  if( n == CURSOR_ARROW )
    cgs.activeCursor = cgs.media.selectCursor;
  else if( n == CURSOR_SIZER )
    cgs.activeCursor = cgs.media.sizeCursor;

  if( cgs.capturedItem )
    Display_MouseMove( cgs.capturedItem, x, y );
  else
    Display_MouseMove( NULL, cgs.cursorX, cgs.cursorY );
}

/*
==================
CG_HideTeamMenus
==================

*/
void CG_HideTeamMenu( void )
{
  Menus_CloseByName( "teamMenu" );
  Menus_CloseByName( "getMenu" );
}

/*
==================
CG_ShowTeamMenus
==================

*/
void CG_ShowTeamMenu( void )
{
  Menus_OpenByName( "teamMenu" );
}

/*
==================
CG_EventHandling
==================
 type 0 - no event handling
      1 - team menu
      2 - hud editor

*/
void CG_EventHandling( int type )
{
  cgs.eventHandling = type;

  if( type == CGAME_EVENT_NONE )
    CG_HideTeamMenu( );
}



void CG_KeyEvent( int key, qboolean down )
{
  if( !down )
    return;

  if( cg.predictedPlayerState.pm_type == PM_NORMAL ||
      ( cg.predictedPlayerState.pm_type == PM_SPECTATOR &&
        cg.showScores == qfalse ) )
  {
    CG_EventHandling( CGAME_EVENT_NONE );
    trap_Key_SetCatcher( 0 );
    return;
  }

  Display_HandleKey( key, down, cgs.cursorX, cgs.cursorY );

  if( cgs.capturedItem )
    cgs.capturedItem = NULL;
  else
  {
    if( key == K_MOUSE2 && down )
      cgs.capturedItem = Display_CaptureItem( cgs.cursorX, cgs.cursorY );
  }
}

int CG_ClientNumFromName( const char *p )
{
  int i;

  for( i = 0; i < cgs.maxclients; i++ )
  {
    if( cgs.clientinfo[ i ].infoValid &&
        Q_stricmp( cgs.clientinfo[ i ].name, p ) == 0 )
      return i;
  }

  return -1;
}

void CG_RunMenuScript( char **args )
{
}


void CG_GetTeamColor( vec4_t *color )
{
  (*color)[ 0 ] = (*color)[ 2 ] = 0.0f;
  (*color)[ 1 ] = 0.17f;
  (*color)[ 3 ] = 0.25f;
}
//END TA UI


/*
================
CG_DrawLighting

================
*/
static void CG_DrawLighting( void )
{
  centity_t   *cent;

  cent = &cg_entities[ cg.snap->ps.clientNum ];

  //fade to black if stamina is low
  if( ( cg.snap->ps.stats[ STAT_STAMINA ] < -800 ) &&
      ( cg.snap->ps.stats[ STAT_PTEAM ] == PTE_HUMANS ) )
  {
    vec4_t black = { 0, 0, 0, 0 };
    black[ 3 ] = 1.0 - ( (float)( cg.snap->ps.stats[ STAT_STAMINA ] + 1000 ) / 200.0f );
    trap_R_SetColor( black );
    CG_DrawPic( 0, 0, 640, 480, cgs.media.whiteShader );
    trap_R_SetColor( NULL );
  }
}

/*
===============================================================================

CENTER PRINTING

===============================================================================
*/


/*
==============
CG_CenterPrint

Called for important messages that should stay in the center of the screen
for a few moments
==============
*/
void CG_CenterPrint( const char *str, int y, int charWidth )
{
  char  *s;

  Q_strncpyz( cg.centerPrint, str, sizeof( cg.centerPrint ) );

  cg.centerPrintTime = cg.time;
  cg.centerPrintY = y;
  cg.centerPrintCharWidth = charWidth;

  // count the number of lines for centering
  cg.centerPrintLines = 1;
  s = cg.centerPrint;
  while( *s )
  {
    if( *s == '\n' )
      cg.centerPrintLines++;

    s++;
  }
}


/*
===================
CG_DrawCenterString
===================
*/
static void CG_DrawCenterString( void )
{
  char  *start;
  int   l;
  int   x, y, w;
  int h;
  float *color;

  if( !cg.centerPrintTime )
    return;

  color = CG_FadeColor( cg.centerPrintTime, 1000 * cg_centertime.value );
  if( !color )
    return;

  trap_R_SetColor( color );

  start = cg.centerPrint;

  y = cg.centerPrintY - cg.centerPrintLines * BIGCHAR_HEIGHT / 2;

  while( 1 )
  {
    char linebuffer[ 1024 ];

    for( l = 0; l < 50; l++ )
    {
      if( !start[ l ] || start[ l ] == '\n' )
        break;

      linebuffer[ l ] = start[ l ];
    }

    linebuffer[ l ] = 0;

    w = CG_Text_Width( linebuffer, 0.5, 0 );
    h = CG_Text_Height( linebuffer, 0.5, 0 );
    x = ( SCREEN_WIDTH - w ) / 2;
    CG_Text_Paint( x, y + h, 0.5, color, linebuffer, 0, 0, ITEM_TEXTSTYLE_SHADOWEDMORE );
    y += h + 6;

    while( *start && ( *start != '\n' ) )
      start++;

    if( !*start )
      break;

    start++;
  }

  trap_R_SetColor( NULL );
}





//==============================================================================

//FIXME: both vote notes are hardcoded, change to ownerdrawn?

/*
=================
CG_DrawVote
=================
*/
static void CG_DrawVote( void )
{
  char    *s;
  int     sec;
  vec4_t  white = { 1.0f, 1.0f, 1.0f, 1.0f };
  char    yeskey[ 32 ], nokey[ 32 ];

  if( !cgs.voteTime )
    return;

  // play a talk beep whenever it is modified
  if( cgs.voteModified )
  {
    cgs.voteModified = qfalse;
    trap_S_StartLocalSound( cgs.media.talkSound, CHAN_LOCAL_SOUND );
  }

  sec = ( VOTE_TIME - ( cg.time - cgs.voteTime ) ) / 1000;

  if( sec < 0 )
    sec = 0;
  Q_strncpyz( yeskey, CG_KeyBinding( "vote yes" ), sizeof( yeskey ) ); 
  Q_strncpyz( nokey, CG_KeyBinding( "vote no" ), sizeof( nokey ) ); 
  s = va( "VOTE(%i): \"%s\"  [%s]Yes:%i [%s]No:%i", sec, cgs.voteString,
    yeskey, cgs.voteYes, nokey, cgs.voteNo );
  CG_Text_Paint( 8, 340, 0.3f, white, s, 0, 0, ITEM_TEXTSTYLE_NORMAL );
}

/*
=================
CG_DrawTeamVote
=================
*/
static void CG_DrawTeamVote( void )
{
  char    *s;
  int     sec, cs_offset;
  vec4_t  white = { 1.0f, 1.0f, 1.0f, 1.0f };
  char    yeskey[ 32 ], nokey[ 32 ];

  if( cg.predictedPlayerState.stats[ STAT_PTEAM ] == PTE_HUMANS )
    cs_offset = 0;
  else if( cg.predictedPlayerState.stats[ STAT_PTEAM ] == PTE_ALIENS )
    cs_offset = 1;
  else
    return;

  if( !cgs.teamVoteTime[ cs_offset ] )
    return;

  // play a talk beep whenever it is modified
  if ( cgs.teamVoteModified[ cs_offset ] )
  {
    cgs.teamVoteModified[ cs_offset ] = qfalse;
    trap_S_StartLocalSound( cgs.media.talkSound, CHAN_LOCAL_SOUND );
  }

  sec = ( VOTE_TIME - ( cg.time - cgs.teamVoteTime[ cs_offset ] ) ) / 1000;

  if( sec < 0 )
    sec = 0;

  Q_strncpyz( yeskey, CG_KeyBinding( "teamvote yes" ), sizeof( yeskey ) ); 
  Q_strncpyz( nokey, CG_KeyBinding( "teamvote no" ), sizeof( nokey ) ); 
  s = va( "TEAMVOTE(%i): \"%s\"  [%s]Yes:%i   [%s]No:%i", sec,
          cgs.teamVoteString[ cs_offset ],
          yeskey, cgs.teamVoteYes[cs_offset],
          nokey, cgs.teamVoteNo[ cs_offset ] );

  CG_Text_Paint( 8, 360, 0.3f, white, s, 0, 0, ITEM_TEXTSTYLE_NORMAL );
}


static qboolean CG_DrawScoreboard( void )
{
  static qboolean firstTime = qtrue;
  float fade, *fadeColor;

  if( menuScoreboard )
    menuScoreboard->window.flags &= ~WINDOW_FORCED;

  if( cg_paused.integer )
  {
    cg.deferredPlayerLoading = 0;
    firstTime = qtrue;
    return qfalse;
  }

  if( cg.showScores ||
      cg.predictedPlayerState.pm_type == PM_INTERMISSION )
  {
    fade = 1.0;
    fadeColor = colorWhite;
  }
  else
  {
    cg.deferredPlayerLoading = 0;
    cg.killerName[ 0 ] = 0;
    firstTime = qtrue;
    return qfalse;
  }


  if( menuScoreboard == NULL )
    menuScoreboard = Menus_FindByName( "teamscore_menu" );

  if( menuScoreboard )
  {
    if( firstTime )
    {
      CG_SetScoreSelection( menuScoreboard );
      firstTime = qfalse;
    }

    Menu_Paint( menuScoreboard, qtrue );
  }

  return qtrue;
}

/*
=================
CG_DrawIntermission
=================
*/
static void CG_DrawIntermission( void )
{
  if( cg_drawStatus.integer )
    Menu_Paint( Menus_FindByName( "default_hud" ), qtrue );

  cg.scoreFadeTime = cg.time;
  cg.scoreBoardShowing = CG_DrawScoreboard( );
}

#define FOLLOWING_STRING "following "

/*
=================
CG_DrawFollow
=================
*/
static qboolean CG_DrawFollow( void )
{
  float       w;
  vec4_t      color;
  char        buffer[ MAX_STRING_CHARS ];

  if( !( cg.snap->ps.pm_flags & PMF_FOLLOW ) )
    return qfalse;

  color[ 0 ] = 1;
  color[ 1 ] = 1;
  color[ 2 ] = 1;
  color[ 3 ] = 1;

  strcpy( buffer, FOLLOWING_STRING );
  strcat( buffer, cgs.clientinfo[ cg.snap->ps.clientNum ].name );

  w = CG_Text_Width( buffer, 0.7f, 0 );
  CG_Text_Paint( 320 - w / 2, 400, 0.7f, color, buffer, 0, 0, ITEM_TEXTSTYLE_SHADOWED );

  return qtrue;
}

/*
=================
CG_DrawQueue
=================
*/
static qboolean CG_DrawQueue( void )
{
  float       w;
  vec4_t      color;
  char        buffer[ MAX_STRING_CHARS ];

  if( !( cg.snap->ps.pm_flags & PMF_QUEUED ) )
    return qfalse;

  color[ 0 ] = 1;
  color[ 1 ] = 1;
  color[ 2 ] = 1;
  color[ 3 ] = 1;

  Com_sprintf( buffer, MAX_STRING_CHARS, "You are in position %d of the spawn queue.",
               cg.snap->ps.persistant[ PERS_QUEUEPOS ] + 1 );

  w = CG_Text_Width( buffer, 0.7f, 0 );
  CG_Text_Paint( 320 - w / 2, 360, 0.7f, color, buffer, 0, 0, ITEM_TEXTSTYLE_SHADOWED );

  if( cg.snap->ps.stats[ STAT_PTEAM ] == PTE_ALIENS )
  {
    if( cgs.numAlienSpawns == 1 )
      Com_sprintf( buffer, MAX_STRING_CHARS, "There is 1 spawn remaining." );
    else
      Com_sprintf( buffer, MAX_STRING_CHARS, "There are %d spawns remaining.",
                   cgs.numAlienSpawns );
  }
  else if( cg.snap->ps.stats[ STAT_PTEAM ] == PTE_HUMANS )
  {
    if( cgs.numHumanSpawns == 1 )
      Com_sprintf( buffer, MAX_STRING_CHARS, "There is 1 spawn remaining." );
    else
      Com_sprintf( buffer, MAX_STRING_CHARS, "There are %d spawns remaining.",
                   cgs.numHumanSpawns );
  }

  w = CG_Text_Width( buffer, 0.7f, 0 );
  CG_Text_Paint( 320 - w / 2, 400, 0.7f, color, buffer, 0, 0, ITEM_TEXTSTYLE_SHADOWED );

  return qtrue;
}

//==================================================================================

#define SPECTATOR_STRING "SPECTATOR"
/*
=================
CG_Draw2D
=================
*/
static void CG_Draw2D( void )
{
  vec4_t    color;
  float     w;
  menuDef_t *menu = NULL, *defaultMenu;

  color[ 0 ] = color[ 1 ] = color[ 2 ] = color[ 3 ] = 1.0f;

  // if we are taking a levelshot for the menu, don't draw anything
  if( cg.levelShot )
    return;

  if( cg_draw2D.integer == 0 )
    return;

  if( cg.snap->ps.pm_type == PM_INTERMISSION )
  {
    CG_DrawIntermission( );
    return;
  }

  //TA: draw the lighting effects e.g. nvg
  CG_DrawLighting( );


  defaultMenu = Menus_FindByName( "default_hud" );

  if( cg.snap->ps.persistant[ PERS_TEAM ] == TEAM_SPECTATOR )
  {
    w = CG_Text_Width( SPECTATOR_STRING, 0.7f, 0 );
    CG_Text_Paint( 320 - w / 2, 440, 0.7f, color, SPECTATOR_STRING, 0, 0, ITEM_TEXTSTYLE_SHADOWED );
  }
  else
    menu = Menus_FindByName( BG_FindHudNameForClass( cg.predictedPlayerState.stats[ STAT_PCLASS ] ) );

  if( !( cg.snap->ps.stats[ STAT_STATE ] & SS_INFESTING ) &&
      !( cg.snap->ps.stats[ STAT_STATE ] & SS_HOVELING ) && menu &&
      ( cg.snap->ps.stats[ STAT_HEALTH ] > 0 ) )
  {
    CG_DrawBuildableStatus( );
    if( cg_drawStatus.integer )
      Menu_Paint( menu, qtrue );

    CG_DrawCrosshair( );
  }
  else if( cg_drawStatus.integer )
    Menu_Paint( defaultMenu, qtrue );

  CG_DrawVote( );
  CG_DrawTeamVote( );
  CG_DrawFollow( );
  CG_DrawQueue( );

  // don't draw center string if scoreboard is up
  cg.scoreBoardShowing = CG_DrawScoreboard( );

  if( !cg.scoreBoardShowing )
    CG_DrawCenterString( );
}

/*
===============
CG_ScalePainBlendTCs
===============
*/
static void CG_ScalePainBlendTCs( float* s1, float *t1, float *s2, float *t2 )
{
  *s1 -= 0.5f;
  *t1 -= 0.5f;
  *s2 -= 0.5f;
  *t2 -= 0.5f;

  *s1 *= cg_painBlendZoom.value;
  *t1 *= cg_painBlendZoom.value;
  *s2 *= cg_painBlendZoom.value;
  *t2 *= cg_painBlendZoom.value;

  *s1 += 0.5f;
  *t1 += 0.5f;
  *s2 += 0.5f;
  *t2 += 0.5f;
}

#define PAINBLEND_BORDER    0.15f

/*
===============
CG_PainBlend
===============
*/
static void CG_PainBlend( void )
{
  vec4_t      color;
  int         damage;
  float       damageAsFracOfMax;
  qhandle_t   shader = cgs.media.viewBloodShader;
  float       x, y, w, h;
  float       s1, t1, s2, t2;

  if( cg.snap->ps.persistant[ PERS_TEAM ] == TEAM_SPECTATOR || cg.intermissionStarted )
    return;

  damage = cg.lastHealth - cg.snap->ps.stats[ STAT_HEALTH ];

  if( damage < 0 )
    damage = 0;

  damageAsFracOfMax = (float)damage / cg.snap->ps.stats[ STAT_MAX_HEALTH ];
  cg.lastHealth = cg.snap->ps.stats[ STAT_HEALTH ];

  cg.painBlendValue += damageAsFracOfMax * cg_painBlendScale.value;

  if( cg.painBlendValue > 0.0f )
  {
    cg.painBlendValue -= ( cg.frametime / 1000.0f ) *
      cg_painBlendDownRate.value;
  }

  if( cg.painBlendValue > 1.0f )
    cg.painBlendValue = 1.0f;
  else if( cg.painBlendValue <= 0.0f )
  {
    cg.painBlendValue = 0.0f;
    return;
  }

  if( cg.snap->ps.stats[ STAT_PTEAM ] == PTE_ALIENS )
    VectorSet( color, 0.43f, 0.8f, 0.37f );
  else if( cg.snap->ps.stats[ STAT_PTEAM ] == PTE_HUMANS )
    VectorSet( color, 0.8f, 0.0f, 0.0f );

  if( cg.painBlendValue > cg.painBlendTarget )
  {
    cg.painBlendTarget += ( cg.frametime / 1000.0f ) *
      cg_painBlendUpRate.value;
  }
  else if( cg.painBlendValue < cg.painBlendTarget )
    cg.painBlendTarget = cg.painBlendValue;

  if( cg.painBlendTarget > cg_painBlendMax.value )
    cg.painBlendTarget = cg_painBlendMax.value;

  color[ 3 ] = cg.painBlendTarget;

  trap_R_SetColor( color );

  //left
  x = 0.0f; y = 0.0f;
  w = PAINBLEND_BORDER * 640.0f; h = 480.0f;
  CG_AdjustFrom640( &x, &y, &w, &h );
  s1 = 0.0f; t1 = 0.0f;
  s2 = PAINBLEND_BORDER; t2 = 1.0f;
  CG_ScalePainBlendTCs( &s1, &t1, &s2, &t2 );
  trap_R_DrawStretchPic( x, y, w, h, s1, t1, s2, t2, shader );

  //right
  x = 640.0f - ( PAINBLEND_BORDER * 640.0f ); y = 0.0f;
  w = PAINBLEND_BORDER * 640.0f; h = 480.0f;
  CG_AdjustFrom640( &x, &y, &w, &h );
  s1 = 1.0f - PAINBLEND_BORDER; t1 = 0.0f;
  s2 = 1.0f; t2 =1.0f;
  CG_ScalePainBlendTCs( &s1, &t1, &s2, &t2 );
  trap_R_DrawStretchPic( x, y, w, h, s1, t1, s2, t2, shader );

  //top
  x = PAINBLEND_BORDER * 640.0f; y = 0.0f;
  w = 640.0f - ( 2 * PAINBLEND_BORDER * 640.0f ); h = PAINBLEND_BORDER * 480.0f;
  CG_AdjustFrom640( &x, &y, &w, &h );
  s1 = PAINBLEND_BORDER; t1 = 0.0f;
  s2 = 1.0f - PAINBLEND_BORDER; t2 = PAINBLEND_BORDER;
  CG_ScalePainBlendTCs( &s1, &t1, &s2, &t2 );
  trap_R_DrawStretchPic( x, y, w, h, s1, t1, s2, t2, shader );

  //bottom
  x = PAINBLEND_BORDER * 640.0f; y = 480.0f - ( PAINBLEND_BORDER * 480.0f );
  w = 640.0f - ( 2 * PAINBLEND_BORDER * 640.0f ); h = PAINBLEND_BORDER * 480.0f;
  CG_AdjustFrom640( &x, &y, &w, &h );
  s1 = PAINBLEND_BORDER; t1 = 1.0f - PAINBLEND_BORDER;
  s2 = 1.0f - PAINBLEND_BORDER; t2 = 1.0f;
  CG_ScalePainBlendTCs( &s1, &t1, &s2, &t2 );
  trap_R_DrawStretchPic( x, y, w, h, s1, t1, s2, t2, shader );

  trap_R_SetColor( NULL );
}

/*
=====================
CG_ResetPainBlend
=====================
*/
void CG_ResetPainBlend( void )
{
  cg.painBlendValue = 0.0f;
  cg.painBlendTarget = 0.0f;
  cg.lastHealth = cg.snap->ps.stats[ STAT_HEALTH ];
}

/*
=====================
CG_DrawActive

Perform all drawing needed to completely fill the screen
=====================
*/
void CG_DrawActive( stereoFrame_t stereoView )
{
  float   separation;
  vec3_t    baseOrg;

  // optionally draw the info screen instead
  if( !cg.snap )
    return;

  switch ( stereoView )
  {
    case STEREO_CENTER:
      separation = 0;
      break;
    case STEREO_LEFT:
      separation = -cg_stereoSeparation.value / 2;
      break;
    case STEREO_RIGHT:
      separation = cg_stereoSeparation.value / 2;
      break;
    default:
      separation = 0;
      CG_Error( "CG_DrawActive: Undefined stereoView" );
  }

  // clear around the rendered view if sized down
  CG_TileClear( );

  // offset vieworg appropriately if we're doing stereo separation
  VectorCopy( cg.refdef.vieworg, baseOrg );

  if( separation != 0 )
    VectorMA( cg.refdef.vieworg, -separation, cg.refdef.viewaxis[ 1 ],
              cg.refdef.vieworg );

  // draw 3D view
  trap_R_RenderScene( &cg.refdef );

  // restore original viewpoint if running stereo
  if( separation != 0 )
    VectorCopy( baseOrg, cg.refdef.vieworg );

  // first person blend blobs, done after AnglesToAxis
  if( !cg.renderingThirdPerson )
    CG_PainBlend( );

  // draw status bar and other floating elements
  CG_Draw2D( );
}



